设计模式原则(SOLID)

软件设计应该按照某些原则,这样子能让软件的可复用性和可维护性更强。

  • 单一职责原则 (Single responsibility principle)

定义

就一个类而言,应该只有一个引起它变化的原因。如果一个类既可以用来渲染游戏界面,又可以用来控制游戏逻辑,我们就说这个类有两个引起它变化的原因。比如有一个UserInfo类,UserInfo类中有设置属性的方法(eg. setName)和行为(eg. sayHello)的方法,我们可以把UserInfo类分为设置属性的类和设置行为的类。

优点
  1. 使用单一职责原则之后,我们修改一个功能,对其他功能的影响显著降低。
  2. 能使我们的代码更模块化,更容易阅读。
  3. 使代码的可维护性更高,因为如果分为不同的职责,我们修改一个职责后,对另外的职责影响就降低很多。
违背的后果
  1. 一个职责使用了外部类库,则使用另外一个职责的用户却也不得不包含这个未被使用的外部类库。
  2. 某个用户由于某个原因需要修改其中一个职责,另外一个职责的用户也将受到影响,他将不得不重新编译和配置
  • 开闭原则 (Open Close Principle)

定义

一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
(PS:对xml和properties的修改不算是对原有代码的修改,因为他们不需要经过编译)

举个栗子

有一只猫,他会说喵喵喵...

public class Cat {
    public void say(){
        System.out.println("喵喵喵...");
    }
}

有一个动物园,我们传入猫,他也会喵喵喵...

public class Zoo {
    public void say(String animal){
        if("cat".equals(animal)){
            Cat cat = new Cat();
            cat.say();
        }
    }
}

如果这时候动物园新来了一只狗...
public class Dog {
public void say(){
System.out.println("汪汪汪...");
}
}
那么我们就要修改Zoo的代码为:

public class Zoo {
    public void say(String animal){
        if("cat".equals(animal)){
            Cat cat = new Cat();
            cat.say();
        }else if("dog".equals(animal)){
            Dog dog = new Dog();
            dog.say();
        }
    }
}

假如以后还有来牛,兔子,老虎,我们就要对这个Zoo类不停的修改...很明显代码的扩展性很差,那么我们可以怎么做呢?
我们可以新增一个抽象类Animal,Animal中有抽象接口say,Cat和Dog继承Animal,Animal作为Zoo的一个成员变量。

public abstract class Animal {
    abstract void say();
}
public class Cat extends Animal{

    @Override
    public void say(){
        System.out.println("喵喵喵...");
    }
}
public class Dog extends Animal{

    @Override
    public void say(){
        System.out.println("汪汪汪...");
    }
}
public class Zoo {

    private Animal animal;

    public void setAnimal(Animal animal) {
        this.animal = animal;
    }

    public void say(){
        animal.say();
    }
}

采用spring的依赖注入功能,以后我们新添一只牛的时候,也只要新添牛的类,然后把spring配置文件的zoo依赖的animal改成牛就可以了。

  • 迪米特原则(Law of Demeter)

定义

  1. 类的朋友
    类自己,类的成员变量,方法参数,方法返回。
  2. 类的陌生类
    指我们在方法中创建的局部变量。
  3. 概念
    一个类应该对陌生类保持最少的了解,只与朋友保持联系,即不要在方法中随便创建无关紧要的类,不要随便调用这些类的方法。如果一个类一定要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
为什么

类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大;通过迪米特原则,可以解耦类与类之间的关系,只有弱耦合,类或者方法的复用率才能提高。

举个栗子

校长需要知道某个班的学生的数量:

/**
  * 校长类
  */
public class Principal {

    public void countOneClassStudent(String className){
        System.out.println("去" + className);
        List<Student> students = getStudents();
        System.out.println("这个班一共有" + students.size());
    }
    
    
    public List<Student> getStudents(){
        List<Student> students = new ArrayList<>();
        students.add(new Student("小明"));
        students.add(new Student("小红"));
        return students;
    }
}

校长平时很忙的,他与学生接触是很少的,更不可能亲自下到某个班里去统计学生的数量,所以学生不是校长的“朋友”。在校长这个类的统计学生数量的方法,校长只需要知道学生的数量,但是却出现了学生的类,这很明显违背了迪米特原则。那什么才是校长的朋友呢?对,就是老师。。。而学生又是老师的朋友。所以我们可以把代码做如下调整:

/**
  * 校长类
  */
public class Principal {

    Map<String, Teacher> teacherMap = new HashMap<>();

    public Teacher getTeacher(String className){
        return teacherMap.get(className);
    }

    public void countOneClassStudent(String className){
        Teacher teacher = getTeacher(className);
        teacher.countOneClassStudent();
    }
}

/**
  * 老师类
  */
public class Teacher {

    private String className;

    public void countOneClassStudent(){
        System.out.println("去" + className);
        List<Student> students = getStudents();
        System.out.println("这个班一共有" + students.size());
    }


    public List<Student> getStudents(){
        List<Student> students = new ArrayList<>();
        students.add(new Student("小明"));
        students.add(new Student("小红"));
        return students;
    }

}
  • 里式替换原则(Liskov Substitution Principle)

定义
  1. 如果我们把代码中所有使用父类的地方都替换成子类,代码还能够执行,不产生错误和异常。
  2. 替换后的代码为什么还能够运行呢?因为子类继承了父类的方法。
  3. 如果要想不产生错误和异常,最好子类不要重写和重载父类的方法,因为父类是已经验证过的代码,子类你自己写的代码没有验证过,不保证一定没有问题。
怎么做
  1. 最好不要重写父类的方法
  2. 如果差不多相同的需求,最好子类另扩展一个不同名字的方法
  3. 如果一定要重写父类的方法,首先要保证方法的参数类型要比原方法的参数类型宽松,即可以是原方法参数的父类;其次要保证重写后的方法的返回值要比原方法的严格,即是它的子类型,最后要保证逻辑没有问题。
  • 接口隔离原则(Interface Segregation Principle)

定义

不能强迫类去实现它们不需要的接口,这样子会增大工作量,即使我们是做空实现也不好;我们可以把一个总接口拆成多个分接口,类只需要实现它们需要的接口即可。

举个栗子

比如家政中心是一个接口,它有打扫卫生,家电维修等功能。我们的保洁阿姨是家政中心的一员,她只会打扫卫生,但是假如我们要保洁阿姨实现家政中心的接口,那么肯定也要实现家电维修的功能,即使它是一个空实现,那么我们可以怎么做呢?我们可以把家政中心的接口拆成打扫卫生和家电维修两个接口(家政中心可以继承打扫卫生和家电维修两个接口),而保洁阿姨只需要实现打扫卫生的接口即可。

  • 依赖倒置原则(Dependence Inversion Principle)

依赖倒置是什么
定义

抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。

倒置?

传统软件开发中,高层模块依赖底层模块,当底层模块发生变化的时候,高层模块也要随之发生变化。比如一个车身依赖一个轮胎,当轮胎的尺寸设计变大以后,车身也要做相应的处理,以适应这个轮胎(车身依赖轮胎)。而依赖倒置就是车身给出一个轮胎的模具,轮胎根据这个模具来实现(轮胎依赖模具)。这样子不管新造的轮胎是怎么样的,车身都不用做任何调整。

举个栗子

网上依赖倒置的例子太多了,就是面向接口编程。

  • 合成复用原则(Composite Reuse Principle)

定义

在面向对象设计中,可以通过两种方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承,但首先应该考虑使用组合/聚合。

怎么做

一般而言,如果两个类之间是“Has-A”的关系应使用组合或聚合,如果是“Is-A”关系可使用继承。"Is-A"是严格的分类学意义上的定义,意思是一个类是另一个类的"一种";而"Has-A"则不同,它表示某一个角色具有某一项责任。

为什么

通过继承来进行复用的主要问题在于继承复用会破坏系统的封装性,因为继承会将基类的实现细节暴露给子类,由于基类的内部细节通常对子类来说是可见的,所以这种复用又称“白箱”复用,如果基类发生改变,那么子类的实现也不得不发生改变;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;而且继承只能在有限的环境中使用(如类没有声明为不能被继承)。

由于组合或聚合关系可以将已有的对象(也可称为成员对象)纳入到新对象中,使之成为新对象的一部分,因此新对象可以调用已有对象的功能,这样做可以使得成员对象的内部实现细节对于新对象不可见,所以这种复用又称为“黑箱”复用,相对继承关系而言,其耦合度相对较低,成员对象的变化对新对象的影响不大,可以在新对象中根据实际需要有选择性地调用成员对象的操作;合成复用可以在运行时动态进行,新对象可以动态地引用与成员对象类型相同的其他对象。

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

推荐阅读更多精彩内容

  • 一、单一职责原则(SRP) 定义:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。 问题由来:...
    真心懒阅读 4,222评论 0 10
  • 什么是设计模式 在GoF(Gang of Four)的书籍《Design Patterns - Elements ...
    zhrowable阅读 1,016评论 0 1
  • 参考资料:菜鸟教程之设计模式 设计模式概述 设计模式(Design pattern)代表了最佳的实践,通常被有经验...
    Steven1997阅读 1,172评论 1 12
  • 从明天起,做一个踏实的人 戒烟,跑步,治疗脱发 从明天起,关注财经和养生堂 我有一纸偏方,助眠安神,滋阴补肾 从明...
    小马爱过河阅读 593评论 0 2
  • 刚刚看了一篇简书朋友写的文章,是关于肖奈和贝微微的,人生有太多不如意,就像,我们没有张小宇,没有余淮,没有肖奈,更...
    你先别急等我变更好阅读 144评论 0 0