三、开闭原则与依赖倒置

设计原则

本文章将会介绍我对一些设计原则的理解,包括:开闭原则、里氏替换原则、迪米特法则、单一职责、接口隔离、合成复用,依赖倒置等进行讲解。

开闭原则(Open-Closed Principle, OCP)

对扩展开放,对修改关闭,这是对开闭原则的基本定义,这个原则存在的意义在于我们需要对一个类的功能进行扩展、增加方法的时候,不用对原本的类进行修改,而是通过继承,去重写,将父类原本方法的行为改造为我们现在阶段需要的行为;也可以通过实现和父类一样的借口来完成一样的效果。造成的结果就是,我们没有去修改原来的类(对修改关闭),却实现了功能的扩展(对扩展开放)。

有一个这样的场景,饭店里面有各式各样的菜品,为了好管理他们,我们定一个最顶层的接口来规范他们应该有什么属性

public interface IFood {
    Integer getId();
    String getName();
    Double getPrice();
}

当然,如果具体到某一道菜的时候,我们就需要去实现这个接口成为一个真正的类才行。

public class TomatoEggFood implements IFood{
    private Integer Id;
    private String name;
    private Double price;
    public TomatoEgg(Integer id, String name, Double price) {
        this.Id = id;
        this.name = name;
        this.price = price;
    }
    public Integer getId() {
        return this.Id;
    }
    public String getName() {
        return this.name;
    }
    public Double getPrice() {
        return this.price;
    }

这样,我们的西红柿鸡蛋就定义好了,我们可以把这个看做是已经在我们系统中已经运行很顺畅的了,可是这个时候,饭店需要打折促销,原来TomatoEgg里面的getPrice方法,也就是这个获得原来价格的行为已经不再满足我们打折促销的需求了,根据开闭原则我们不能随便对这个进行修改,因为这个在系统上运行良好的类,如果因为我们的擅自修改而产生了问题,会是一件很麻烦的事情。

所以,我们通过继承这个TomatoEgg来重新修改他的获得价钱的行为。

public class TomatoEggDiscountFood extends TomatoEggFood {
    public JavaDiscountCourse(Integer id, String name, Double price) {
        super(id, name, price);
    }
    public Double getOriginPrice(){//这里其实违背了里氏替换法则,后会讲解,这里留个坑
        return super.getPrice();
    }
    public Double getPrice(){
        return super.getPrice() * 0.8;//修改的行为
    }
}

这里,当我们想要使用打折后的TomatoEgg的时候,完全可以使用我们扩展TomatoEggDiscountFood来获得一个打折后的价钱,而不需要对着TomatoEgg进行再次的修改而避免系统其它地方因为使用了他的getPrice而出现问题。

依赖倒置原则(Dependence Inversion Principle,DIP)

指设计代码结构时,高层模块不应该依赖底层模块,二者都应该依赖其抽象。抽象不应该依赖细节;细节应该依赖抽象

简单来说就是。

高层模块不应该依赖底层模块:类不能和类直接关联起来,这样耦合性太强,不利于以后的升级和拓展。

二者都应该依赖其抽象:他们应该都有一个共同的父类,抽象,接口是个很好的选择,因为具体行为还未实现,有着无限的可能性。

抽象不应该依赖细节:作为定义的规范,我并不在意你到底会去如何实现我的规范,对你的实现细节不感兴趣。

细节应该依赖抽象:作为实现的一方,必须严格按照指定的规范去完成接口和抽象类的前置参数和后置返回值。

还是用饭店举例子,我去饭店吃饭。我自己想吃打折的番茄炒蛋还有甜点。

那么这个我(Pop)的例子就可以简单写成这样。

public class Pop {
    public void eatTomatoEgg(){
        System.out.println("我吃番茄炒蛋");
    }
    public void eatCake(){
        System.out.println("我吃甜点");
    }
}

那么主函数里面,我要吃东西,就需要进行调用方法操作。

public static void main(String[] main){
    Pop pop = new Pop();
    pop.eatTomatoEgg();
    pop.eatCake();
}

以上,我完成了吃饭的动作,不过这个时候,我吃番茄炒蛋太腻了,我还想吃一碗米饭,这个时候我们需要怎么做呢,最简单的方法就是在Pop类新添加eatRice方法,接着再到主函数调用即可。可是凡事我们都需要站到更高的角度去思考问题,如果我是一个大吃货,我要吃的东西非常多,那么这个类中的方法会非常的冗长,并且还是那句话,这个类如果在系统上运行良好,你去修改这个类必定会伴随着相应的风险,而这些风险可能是我们无法承受的,所以这个架构就存在着问题,我们需要重新设计层级关系。

回到依赖倒置的原则的第一条:高层模块不应该依赖底层模块,高层模块指的是调用方,我们调用的时候非常舒服,因为吃东西这个业务逻辑已经被我们自己封装起来了,只需要调用一个方法就可以完成我们的吃东西动作,但是我们发现这是不利于我们拓展的,借用开闭原则这是对修改很不友好的,我们紧紧的将吃饭的人食物这两个概念紧紧耦合在一起,我们调用(高层模块)的时候太依赖一个调用方(底层模块)来帮我们完成操作,来导致这个类中所涉及的诸多业务解耦起来困哪。

那么如何解决,二者都应该依赖其抽象,现在我们来看看那些概念可以被抽象,首先食物依旧可以被抽象,那么我们可以这样做。

public interface IFood{
    void eat();
}

我们把食物单独抽象出来,不让他依赖与于食客

public class TomatoEgg implements IFood{
    @Override
    public void eat(){
        System.out.println("吃番茄炒蛋");
    }
}
public class Cake implements IFood{
    @Override
    public void eat(){
        System.out.println("吃蛋糕");
    }
}
public class Rice implements IFood{
    @Override
    public void eat(){
        System.out.println("吃饭饭");
    }
}

将对象的修改一下。

public class Pop{
    public void eat(IFood food){
        food.eat();
    }
}

现在主方法的调用就变成为了。

public static void main(String[] ars){
    Pop pop = new Pop();
    pop.eat(new TomatoEgg());
    pop.eat(new Rice());//扩展也变得方便,也不需要直接去修改底层类。
    pop.eat(new Cake());
}

以上,我们都在依赖IFood这个接口,只要我们一增加需求,只需要直接new给食客就可以。

抽象不应该依赖细节的解释可以得到更好的体现,对于IFood中对应的eat方法,并不关心你到底要吃什么,怎么吃,反正这是一个吃的动作,并不关心你的具体细节,话虽这么说,但是很多业务场景的抽象出来的规范很难拿捏,你并不确定这个你制定的规范是否适用于绝大部分场景,这个时候我们是否就需要抽出更高层的接口,又或者通过继承接口来产生适用性更强大的后代。

来说几个衍生的概念,依赖注入setter注入。

其实这并不算是什么的新的概念,依赖注入我相信很多人第一个想到的就是spring中的DI

虽然后者的DI是通过IOC容器中存储的BeanDefintion信息来实例化并注入到相应声明的变量中去,但是我现在说的依赖注入没有spring那么复杂。

这个依赖注入我的理解就是实例化时必须依赖某个类,并且调用方法的时候依赖那个被注入类的具体实现。。

我们将Pop改造一下。

public class Pop {
    private IFood food;
    public Pop(IFood food) {
        this.food = food;
    }
    public void eat(){
        food.eat();
    }
}
public static void main(String[] ars){
    Pop pop = new Pop(new Cake());
    pop.eat();
}

在构造方法中,我们必须传入IFood的一个具体实现,才允许你实例Pop ,接着我们的eat真正的实现也是依赖于我们传入具体实现细节的。

一眼看过去可能觉得这个依赖注入也没什么复杂的,甚至觉得有些多余,不过很多源码的设计,都有这种设计的影子,这样的设计我觉得还是为了隐藏,和封装实现的细节,而让调用者无须关心更多的内容,我只要传入了参数,调用就可以获得我需要的吃东西效果,即便传入的所依赖的东西各式各样,我也只需要调用一个简单的eat就可以让他````自我发挥```了。

接下来是setter注入,本质上其实还是一个东西,这个情况建立在Pop为单例的情况下,因为不能new出来,所以放入所依赖的模块,必须设置进去,于是就有了。

public class Pop {
    private IFood food;
    public void setFood(IFood food) {
        this.food = food;
    }
    public void eat(){
        food.eat();
    }
    private Pop(){}
    public static Pop getInstance(){
        return new Pop();
    }
}
public static void main(String[] ars){
    Pop pop = Pop.getInstance();
    pop.setFood(new Cake());
    pop.eat();
}

版权声明:本文为CSDN博主「PopCandier」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/CandyCCCation/article/details/88940953

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容