这是我参与更文挑战的第14天,活动详情查看: 更文挑战
设计模式
在设计模式一我们已经讲了两种设计原则,接下来继续讲解。
依赖倒转原则
依赖于抽象而不依赖于具体,核心思想面向接口编程。目的是制定好规范(设计),而不是实现。
比如我们现在写的代码要自动化部署到服务器,开始我们使用的github进行的自动化部署。实现过程就是程序员–>提交代码到github–>自动化部署。
程序员–>提交代码
github–>自动化部署
错误示例
package com.wangscaler.dependenceinversion;
/**
* @author wangscaler
* @date 2021.06.16 17:43
*/
public class DependenceInversionPrinciple {
public static void main(String[] args) {
Programmer programmer = new Programmer();
programmer.commit(new Github());
}
static class Github {
public String cicd() {
return "github 自动化部署完成";
}
}
static class Programmer {
public void commit(Github github) {
System.out.println(github.cicd());
}
}
}
复制代码
有一天,github仓库访问太慢了,我们不想用了,换成gitlab,这时候我们新建一个Gitlab仓库并加上的cicd方法,但是我们虽然有了这个仓库,却没法自动化部署,因为我们的程序员只知道github。此时Programmer依赖了Github,这样是不合理的,模块与模块之间耦合度太高,生产力太低。
正确示例
package com.wangscaler.dependenceinversion;
/**
* @author wangscaler
* @date 2021.06.16 17:43
*/
public class DependenceInversionPrinciple1 {
public static void main(String[] args) {
Programmer programmer = new Programmer();
programmer.commit(new Gitlab());
}
static class Github implements IWarehouse {
public String cicd() {
return "github 自动化部署完成";
}
}
static class Gitlab implements IWarehouse {
public String cicd() {
return "gitlab 自动化部署完成";
}
}
public interface IWarehouse {
public String cicd();
}
static class Programmer {
public void commit(IWarehouse warehouse) {
System.out.println(warehouse.cicd());
}
}
}
复制代码
因为github和gitlab都属于仓库,而且都是有cicd的方法,所以定义仓库接口,让他们实现这个接口就行了。如果再后来,又想换成华为云仓库,只需要增加华为云这个类就行了。
package com.wangscaler.dependenceinversion;
/**
* @author wangscaler
* @date 2021.06.16 17:43
*/
public class DependenceInversionPrinciple1 {
public static void main(String[] args) {
Programmer programmer = new Programmer();
programmer.commit(new Huawei());
}
static class Github implements IWarehouse {
public String cicd() {
return "github 自动化部署完成";
}
}
static class Gitlab implements IWarehouse {
public String cicd() {
return "gitlab 自动化部署完成";
}
}
static class Huawei implements IWarehouse {
public String cicd() {
return "华为云仓库自动化部署完成";
}
}
public interface IWarehouse {
public String cicd();
}
static class Programmer {
public void commit(IWarehouse warehouse) {
System.out.println(warehouse.cicd());
}
}
}
复制代码
总结: 根据依赖倒转原则,我们关注的抽象而不是具体。在这个例子当中,我们的设计思路应该是程序员–>提交代码到仓库–>自动化部署。无论是github、gitlab还是华为云,他们抽象出来都是仓库即从下层模块github开始,想想看他能抽象化出什么。这个抽象类就相当于一个缓冲层,增强代码的可扩展性。
里氏替换原则
所有引用基类的地方都能透明的使用他的子类。面向对象设计的基本原则之一。任何基类可以出现的地方,子类一定可以出现,而派生类也能够在基类的基础上增加新的行为。会降低程序的移植性,增加程序的耦合性(基类的修改会影响到所有子类)。子类继承父类时,尽量不要重写父类的方法。
比如我们有两个鸟,燕子和奇异鸟(不会飞),本来我们常识鸟都是飞的,所以代码这样写的
package com.wangscaler.liskovsubstitution;
/**
* @author wangscaler
* @date 2021.06.17
*/
public class LiskovSubstitutionPrinciple {
public static void main(String[] args) {
Swallow swallow = new Swallow();
Kiwi kiwi = new Kiwi();
swallow.setSpeed(110);
kiwi.setSpeed(120);
System.out.println(swallow.getFlyTime(240));
System.out.println(kiwi.getFlyTime(240));
}
static class Bird {
double speed;
public void setSpeed(double speed) {
this.speed = speed;
}
public double getFlyTime(double distance) {
return (distance / speed);
}
}
static class Swallow extends Bird {
}
static class Kiwi extends Bird {
@Override
public void setSpeed(double speed) {
speed = 0;
}
}
}
复制代码
执行结果
2.1818181818181817
Infinity
复制代码
因为奇异鸟不会飞,所以改写了父类的方法,导致我们在父类设置速度,理所当然认为在奇异鸟中设置速度也是这个,最终导致错误的产生,我们应该设置更基础的父类,来避免字类继承的时候重写父类的方法。
package com.wangscaler.liskovsubstitution;
/**
* @author wangscaler
* @date 2021.06.17
*/
public class LiskovSubstitutionPrinciple {
public static void main(String[] args) {
Swallow swallow = new Swallow();
Kiwi kiwi = new Kiwi();
swallow.setFlySpeed(110);
kiwi.setSpeed(120);
System.out.println(swallow.getFlyTime(240));
System.out.println(kiwi.getTime(240));
}
static class Animal {
double speed;
public void setSpeed(double speed) {
this.speed = speed;
}
public double getTime(double distance) {
return (distance / speed);
}
}
static class Bird extends Animal {
double flyspeed;
public void setFlySpeed(double speed) {
this.flyspeed = speed;
}
public double getFlyTime(double distance) {
return (distance / flyspeed);
}
}
static class Swallow extends Bird {
}
static class Kiwi extends Animal {
}
}
复制代码
总结: 里氏替换原则就是要求我们集成基类,尽量不要重写父类,可以增加功能。如果必须要重写父类方法,一定要符合输入条件比父类更加宽松,输出条件比父类更加严格的要求。比如一开始的代码,父类Bird的输入条件是范围,而子类Kiwi的输入条件变成了0显然是不符合规则的。