一天一个设计模式---分类与六大原则

原创 2016年08月31日 14:37:19

一、设计模式的分类

设计模式可以分为三大类:

(1) 创建型模式

  1. 抽象工厂模式:http://blog.csdn.net/oDeviloo/article/details/52471559
  2. 生成器模式:http://blog.csdn.net/oDeviloo/article/details/52684935
  3. 工厂方法模式:http://blog.csdn.net/oDeviloo/article/details/52464359
  4. 原型模式:http://blog.csdn.net/oDeviloo/article/details/52760783
  5. 单例模式:http://blog.csdn.net/oDeviloo/article/details/52388833

(2) 结构型模式

  1. 适配器模式:http://blog.csdn.net/oDeviloo/article/details/52535105
  2. 桥接模式:http://blog.csdn.net/oDeviloo/article/details/52816069
  3. 组合模式:http://blog.csdn.net/oDeviloo/article/details/52839170
  4. 装饰者模式:http://blog.csdn.net/oDeviloo/article/details/52488283
  5. 外观模式:http://blog.csdn.net/oDeviloo/article/details/52918773
  6. 享元模式:http://blog.csdn.net/oDeviloo/article/details/52458677
  7. 代理模式:http://blog.csdn.net/oDeviloo/article/details/52420690

(3) 行为模式

  1. 职责链模式:http://blog.csdn.net/oDeviloo/article/details/53022407
  2. 命令模式:http://blog.csdn.net/oDeviloo/article/details/53081455
  3. 解释器模式:http://blog.csdn.net/oDeviloo/article/details/53184547
  4. 迭代器模式:http://blog.csdn.net/odeviloo/article/details/53292643
  5. 中介者模式:http://blog.csdn.net/odeviloo/article/details/53292575
  6. 备忘录模式:http://blog.csdn.net/oDeviloo/article/details/53408610
  7. 观察者模式:http://blog.csdn.net/oDeviloo/article/details/52507234
  8. 状态模式:http://blog.csdn.net/oDeviloo/article/details/52600518
  9. 策略模式:http://blog.csdn.net/oDeviloo/article/details/52610961
  10. 模板方法模式:http://blog.csdn.net/oDeviloo/article/details/52671340
  11. 访问者模式:http://blog.csdn.net/oDeviloo/article/details/53462758


设计模式之间关系
图片出处《设计模式》一书

二、设计模式六大原则

1. 单一职责原则

定义:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。如果一个类包含多种职责,就应该把类拆分。

场景:如果类A有两个职责:d1,d2。当职责d1需要修改时,可能会导致原本运行正常的职责d2功能产生问题。

方案:如果一个类包含多种职责,就应该把类拆分。分别建立两个类A、B,让A负责d1,B负责d2。当需要修改某一职责,那么将不会对另外一个功能产生影响。

2. 里氏替换原则

定义:这一原则与继承紧密相关。如果对每一个类型为 T1的对象 o1,都有类型为 T2 的对象o2,使得以 T1定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。所有引用基类的地方必须能透明地使用其子类的对象。

场景:有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。

// 我们需要完成一个两数相减的功能,由类A来负责。
class A{
    public int func1(int a, int b){
        return a-b;
    }
}

public class Client{
    public static void main(String[] args){
        A a = new A();
        System.out.println("100-50="+a.func1(100, 50));
        System.out.println("100-80="+a.func1(100, 80));
    }
} 
运行结果:

100-50=50

100-80=20

如果我们需要增加一个新的功能:完成两数相加,然后再与100求和,由类B来负责。

那么类B则要完成两个功能:
- 两数相减
- 两数相加,然后再加100

由于类A已经实现了第一个功能,所以类B继承类A后,只需要再完成第二个功能就可以了,代码如下:

class B extends A{
    public int func1(int a, int b){
        return a+b;
    }

    public int func2(int a, int b){
        return func1(a,b)+100;
    }
}

public class Client{
    public static void main(String[] args){
        B b = new B();
        System.out.println("100-50="+b.func1(100, 50));
        System.out.println("100-80="+b.func1(100, 80));
        System.out.println("100+20+100="+b.func2(100, 20));
    }
} 
类B完成后,运行结果:

100-50=150

100-80=180

100+20+100=220

方案:当使用继承时,遵循里氏替换原则。子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下4层含义:

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
  • 子类中可以增加自己特有的方法。
  • 当子类的方法重载父类的方法时,方法的形参要比父类方法的输入参数更宽松。
  • 当子类的方法实现父类的抽象方法时,方法的返回值要比父类更严格。

3. 依赖倒置原则

定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。

场景:类A的方法依赖类B,如果需要A通过C来实现同样的功能,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。


class Book{
    public String getContent(){
        return "书中的故事";
    }
}

class Mother{
    public void narrate(Book book){
        System.out.println(book.getContent());
    }
}

public class Client{
    public static void main(String[] args){
        Mother mother = new Mother();
        mother.narrate(new Book());
    }
} 
运行结果:

书中的故事

假如有一天,我们不是给书而是给一份报纸,让这位母亲读下报纸上的事。报纸代码如下:

class Newspaper{
    public String getContent(){
        return "报纸上的新闻";
    }
} 

我们会发现一个问题,我们需要修改原来的narrate,将入参修改。或者,我们可以新建一个方法。但是,这样也需要修改Mother的代码。使用面向接口编程,会很好的解决这个问题

public void narrate(Newspaper papper){
        System.out.println(papper.getContent());
}

方案:通过面向接口编程,将方法的参数类型设置为接口。而之前的具体实现类来实现接口中的方法。

class Book implements IReader{
    public String getContent(){
        return "书中的故事";
    }
}

class Newspaper implements IReader{
    public String getContent(){
        return "报纸上的新闻";
    }
} 

class Mother{
    public void narrate(IReader reader){
        System.out.println(reader.getContent());
    }
}

public class Client{
    public static void main(String[] args){
        Mother mother = new Mother();
        mother.narrate(new Book());
        mother.narrate(new Newspaper());
    }
}

4. 接口隔离原则

定义:实现类不应该依赖它不需要实现接口具体方法的接口。

场景:接口A需要实现方法a、b、c,具体实现类B实现方法a、b,实现类C实现a、c。所以B,C需要实现它们不需要的方法。

方案:将接口A继续拆分为独立的几个接口,类B和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。

5. 迪米特法则(最少知道原则)

定义:一个对象应该对其他对象保持最少的了解。也就是说:一个类对自己依赖的类知道的越少越好。也就是说无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。

场景:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

// 总公司员工
public class Employee {
}

// 子公司员工
public class ChildEmployee {
}


public class ChildEmployeeService {
    // 输出所有子公司员工
    public void printAllChildEmployee() {
        List<ChildEmployee> list = new ArrayList<ChildEmployee>();
        for(int i=0; i<30; i++){
            ChildEmployee emp = new ChildEmployee();
            list.add(emp);
        }

        for (ChildEmployee childEmployee : list) {
            System.out.println(childEmployee);
        }
    }

}


public class EmployeeService {
    // 输出所有总公司员工
    public void printAllEmployee() {
        List<Employee> list = new ArrayList<Employee>();
        for (int i = 0; i < 30; i++) {
            Employee emp = new Employee();
            list.add(emp);
        }

        for (Employee employee : list) {
            System.out.println(employee);
        }
    }

    // 输出所有公司员工
    public void printEmployee() {
        // 输出所有子公司员工
        List<ChildEmployee> list = new ArrayList<ChildEmployee>();
        for (int i = 0; i < 30; i++) {
            ChildEmployee emp = new ChildEmployee();
            list.add(emp);
        }

        for (ChildEmployee childEmployee : list) {
            System.out.println(childEmployee);
        }

        // 输出所有总公司员工
        List<Employee> list2 = new ArrayList<Employee>();
        for (int i = 0; i < 30; i++) {
            Employee emp = new Employee();
            list2.add(emp);
        }

        for (Employee employee : list2) {
            System.out.println(employee);
        }
    }
}

// 测试用例
public class Test {
    public static void main(String[] args) {
        EmployeeService e = new EmployeeService();
        e.printEmployee();
    }
}

现在这个设计的主要问题出在EmployeeService中,根据迪米特法则,只与直接的朋友发生通信,而ChildEmployee类和EmployeeService类并不应该有直接关系,从逻辑上讲总公司只与他的分公司耦合就行了,与分公司的员工并没有任何联系,而和分公司员工的相关操作应该交给分公司来处理。

方案:软件编程的总的原则:低耦合,高内聚。尽量降低类与类之间的耦合。

// 修改EmployeeService类printEmployee方法,让总公司调用子公司的业务方法来实习和子公司员工的解耦合
public void printEmployee(ChildEmployeeService service) {
        // 输出所有子公司员工
        service.printAllChildEmployee();

        // 输出所有总公司员工
        List<Employee> list2 = new ArrayList<Employee>();
        for (int i = 0; i < 30; i++) {
            Employee emp = new Employee();
            list2.add(emp);
        }

        for (Employee employee : list2) {
            System.out.println(employee);
        }
 }

public class Test {
    public static void main(String[] args) {
        EmployeeService e = new EmployeeService();
        e.printEmployee(new ChildEmployeeService());
    }
}

6. 开闭原则

定义:对扩展开放,对修改关闭

场景:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误

方案:当软件需要变化时,不应该通过修改已有的代码来实现变化,而是尽量通过扩展原有代码。这是为了使程序的扩展性好,易于维护和升级。

版权声明:本文为博主原创文章,转载请注明本文地址。

相关文章推荐

设计模式(一)设计六大原则

1. 单一职责原则(SRP) 定义:就一个类而言,应该仅有一个引起它变化的原因。 从这句定义我们很难理解它的含义,通俗讲就是我们不要让一个类承担过多的职责。如果一个类承担的职责过多,就...

设计模式六大原则(6):开闭原则

定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。 问题由来:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对...
  • zhengzhb
  • zhengzhb
  • 2012年02月27日 08:48
  • 71653

Java设计模式——六大原则

设计模式6大原则:里氏置换原则 里氏置换原则(Liskov Substitution Principle),简称LSP。所有引用基类的地方必须能够透明的使用其子类对象。也就是说,只要父类出现的地方子类...

设计模式六大原则

01 单一职责原则一个类只负责单一功能02 里氏替换原则子类对象在任何场景下都能替换父类对象; 不要覆盖父类已经实现的方法03 依赖倒转高层模块不应该依赖低层模块的实现,二者都应该依赖抽象; 抽象不应...

设计模式六大原则(1):单一职责原则

定义:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。 问题由来:类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常...
  • zhengzhb
  • zhengzhb
  • 2012年02月21日 09:52
  • 84529

设计模式的六大原则

概括: 原则 含义 具体方法 开闭原则 对扩展开放,对修改关闭 多使用抽象类和接口 里氏代换原则 基类可以被子类替换...

设计模式分类以及六大原则

设计模式的分类总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、...
  • itlwc
  • itlwc
  • 2016年06月26日 23:38
  • 3123

设计模式总结之模式分类

话说软件江湖还是混沌之时,无门无派,无招无式,GoF(Gang of Four,四个大师),穷理之熟,融会贯通,合著了《Design Patterns: Elements of Reusable Ob...

23种设计模式汇总整理

设计模式分为三大类:创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。行为型...

设计模式的三种类型

设计模式分为三种类型,共23类。 一、创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。 二、结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模...
  • cyjch
  • cyjch
  • 2016年05月26日 13:29
  • 1952
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:一天一个设计模式---分类与六大原则
举报原因:
原因补充:

(最多只允许输入30个字)