原创

设计模式-七大设计原则

1. 单一职责原则

【单一职责原则】:各司其职,一个类只负责一项职责。

  1. 降低类的复杂度,一个类只负责一项职责。
  2. 提高类的可读性,可维护性。
  3. 降低变更引起的风险。
  4. 通常情况下,我们应当遵守单一职责原则,只有在逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则。

demo1-存在的问题

先来看一个类,看看发现其中的问题:

package com.lzhpo.principle.singleresponsibility;

/**
 * @author lzhpo
 */
public class SingleResponsibility1 {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
        vehicle.run("摩托车");
        vehicle.run("汽车");
        vehicle.run("飞机");
    }
}

/**
 * 交通工具类
 */
class Vehicle {
    public void run(String vehicle) {
        System.out.println(vehicle + "在公路上运行...");
    }
}

运行结果:

摩托车在公路上运行...
汽车在公路上运行...
飞机在公路上运行...

可以看到,它违反了单一职责原则,没有做到一个类只负责一项职责。

demo2-遵循单一职责原则

package com.lzhpo.principle.singleresponsibility;

/**
 * 单一职责原则:各司其职。
 * <p>
 *     1. 降低类的复杂度,一个类只负责一项职责。
 *     2. 提高类的可读性,可维护性。
 *     3. 降低变更引起的风险。
 *     4. 通常情况下,我们应当遵守单一职责原则,只有在逻辑足够简单,才可以在代码级违
 *        反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则。
 * </p>
 * <p>
 * 此方案遵守了单一职责原则,修复了{@link SingleResponsibility1},但是开销大。
 * </p>
 *
 * @author lzhpo
 */
public class SingleResponsibility2 {
    public static void main(String[] args) {
        RoadVehicle roadVehicle = new RoadVehicle();
        roadVehicle.run("摩托车");

        AirVehicle airVehicle = new AirVehicle();
        airVehicle.run("飞机");

        WaterVehicle waterVehicle = new WaterVehicle();
        waterVehicle.run("鱼儿");
    }
}

class RoadVehicle {
    public void run(String vehicle) {
        System.out.println(vehicle + "在公路运行...");
    }
}

class AirVehicle {
    public void run(String vehicle) {
        System.out.println(vehicle + "在天空运行...");
    }
}

class WaterVehicle {
    public void run(String vehicle) {
        System.out.println(vehicle + "在水里运行...");
    }
}

运行结果:

摩托车在公路运行...
飞机在天空运行...
鱼儿在水里运行...

但是,虽然此方案遵守了单一职责原则,修复了SingleResponsibility1的问题,但是开销大。

demo3-遵循单一职责原则

package com.lzhpo.principle.singleresponsibility;

/**
 * <p>
 *     弥补了{@link SingleResponsibility2}
 *     在方法级别上遵守了单一职责原则,但是在类上没有遵守单一职责原则,
 *     但是它也是遵守单一职责原则的。
 * </p>
 * @author lzhpo
 */
public class SingleResponsibility3 {
    public static void main(String[] args) {
        Vehicle2 vehicle2 = new Vehicle2();
        vehicle2.runWater("鱼儿");
        vehicle2.runRoad("摩托车");
        vehicle2.runAir("飞机");
    }
}

class Vehicle2 {
    public void runRoad(String vehicle) {
        System.out.println(vehicle +"在公路上运行...");
    }
    public void runAir(String vehicle) {
        System.out.println(vehicle +"在公路上运行...");
    }
    public void runWater(String vehicle) {
        System.out.println(vehicle +"在公路上运行...");
    }
}

运行结果:

鱼儿在公路上运行...
摩托车在公路上运行...
飞机在公路上运行...

在方法级别上遵守了单一职责原则,但是在类上没有遵守单一职责原则,但是它也是遵守单一职责原则的。

2. 接口隔离原则

【接口隔离原则】:

客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。

demo1-存在的问题

package com.lzhpo.principle.segregation;

/**
 * @author lzhpo
 */
public class Segregation1 {
    public static void main(String[] args) {

    }
}

/**
 * 接口
 */
interface Interface1 {
    void operation1();

    void operation2();

    void operation3();

    void operation4();

    void operation5();
}

/**
 * B
 */
class B implements Interface1 {
    @Override
    public void operation1() {
        System.out.println("B实现了operation1...");
    }

    @Override
    public void operation2() {
        System.out.println("B实现了operation2...");
    }

    @Override
    public void operation3() {
        System.out.println("B实现了operation3...");
    }

    @Override
    public void operation4() {
        System.out.println("B实现了operation4...");
    }

    @Override
    public void operation5() {
        System.out.println("B实现了operation5...");
    }
}

/**
 * D
 */
class D implements Interface1 {
    @Override
    public void operation1() {
        System.out.println("D实现了operation1...");
    }

    @Override
    public void operation2() {
        System.out.println("D实现了operation2...");
    }

    @Override
    public void operation3() {
        System.out.println("D实现了operation3...");
    }

    @Override
    public void operation4() {
        System.out.println("D实现了operation4...");
    }

    @Override
    public void operation5() {
        System.out.println("D实现了operation5...");
    }
}

/**
 * A
 * <p>
 * A类通过接口Interface1依赖使用B类,
 * 但是只会使用到1、2、3方法。
 * </p>
 */
class A {
    public void depend1(Interface1 i) {
        i.operation1();
    }

    public void depend2(Interface1 i) {
        i.operation2();
    }

    public void depend3(Interface1 i) {
        i.operation3();
    }
}

/**
 * C
 * <p>
 * C类通过接口Interface1依赖(使用)D类,
 * 但是只会使用到1、4、5方法。
 * </p>
 */
class C {
    public void depend1(Interface1 i) {
        i.operation1();
    }

    public void depend4(Interface1 i) {
        i.operation4();
    }

    public void depend5(Interface1 i) {
        i.operation5();
    }
}

依赖关系UML类图:

引入接口隔离原则-1.png

问题:多余的接口、方法,没有做到一个类对另一个类的依赖建立在最小的接口上。

demo2-遵循接口隔离原则

package com.lzhpo.principle.segregation.improve;

/**
 * 接口隔离原则
 * <p>
 * 客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
 * </p>
 * @author lzhpo
 */
public class Segregation2 {
    public static void main(String[] args) {
        A a = new A();
        a.depend1(new B());
        a.depend2(new B());
        a.depend3(new B());

        C c = new C();
        c.depend1(new D());
        c.depend4(new D());
        c.depend5(new D());
    }
}

/**
 * 接口1
 */
interface Interface1 {
    void operation1();
}

/**
 * 接口2
 */
interface Interface2 {
    void operation2();
    void operation3();
}

/**
 * 接口3
 */
interface Interface3 {
    void operation4();
    void operation5();
}

/**
 * B
 */
class B implements Interface1, Interface2 {
    @Override
    public void operation1() {
        System.out.println("B实现了operation1...");
    }

    @Override
    public void operation2() {
        System.out.println("B实现了operation2...");
    }

    @Override
    public void operation3() {
        System.out.println("B实现了operation3...");
    }
}

/**
 * D
 */
class D implements Interface1, Interface3 {
    @Override
    public void operation1() {
        System.out.println("D实现了operation1...");
    }

    @Override
    public void operation4() {
        System.out.println("D实现了operation4...");
    }

    @Override
    public void operation5() {
        System.out.println("D实现了operation5...");
    }
}

/**
 * A
 * <p>
 * A类通过接口Interface1依赖使用B类,
 * 但是只会使用到1、2、3方法。
 * </p>
 */
class A {
    public void depend1(Interface1 i) {
        i.operation1();
    }

    public void depend2(Interface2 i) {
        i.operation2();
    }

    public void depend3(Interface2 i) {
        i.operation3();
    }
}

/**
 * C
 * <p>
 * C类通过接口Interface1依赖(使用)D类,
 * 但是只会使用到1、4、5方法。
 * </p>
 */
class C {
    public void depend1(Interface1 i) {
        i.operation1();
    }

    public void depend4(Interface3 i) {
        i.operation4();
    }

    public void depend5(Interface3 i) {
        i.operation5();
    }
}

运行结果:

B实现了operation1...
B实现了operation2...
B实现了operation3...
D实现了operation1...
D实现了operation4...
D实现了operation5...

UML依赖图:

引入接口隔离原则-2.png

3. 依赖倒转原则

【 依赖倒转原则】:

  1. 高层模块不应该依赖低层模块,二者都应该依赖其抽象。
  2. 抽象不应该依赖细节,细节应该依赖抽象。
  3. 依赖倒转(倒置)的中心思想是面向接口编程。
  4. 依赖倒转原则的理念:相对于细节的多边形,抽象的东西要稳定的多。
  5. 以抽象为基础搭建的架构比细节为基础的架构要稳定的多。
  6. 在Java中,抽象是指的是接口或抽象类,细节就是具体的实现类。
  7. 使用接口或抽象类的目的是指定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。

依赖关系传递的三种方式:

  1. 接口传递。
  2. 构造方法传递。
  3. setter方式传递。

demo1-存在的问题

package com.lzhpo.principle.inversion;

/**
 * @author lzhpo
 */
public class DependecyInversion {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new Email());
    }
}

class Email {
    public String getInfo() {
        return "电子邮件信息:HelloWorld";
    }
}

class Person {
    public void receive(Email email) {
        System.out.println(email.getInfo());
    }
}

运行结果:

电子邮件信息:HelloWorld

问题:如果我们增加一个微信类,同时也需要增加微信类对应的方法。

demo2-遵循依赖倒转原则

package com.lzhpo.principle.inversion.improve;

/**
 * @author lzhpo
 */
public class DependecyInversion {
    public static void main(String[] args) {
        Person person = new Person();
        person.receive(new Email());

        person.receive(new WeChat());
    }
}

interface IReceiver {
    public String getInfo();
}

class Email implements IReceiver {
    @Override
    public String getInfo() {
        return "电子邮件信息:HelloWorld";
    }
}

// 增加WeChat
class WeChat implements IReceiver {

    @Override
    public String getInfo() {
        return "微信信息:HelloWorld";
    }
}

class Person {
    public void receive(IReceiver iReceiver) {
        System.out.println(iReceiver.getInfo());
    }
}

运行结果:

电子邮件信息:HelloWorld
微信信息:HelloWorld

4. 里式替换原则

【里式替换原则】:

  1. 1988年,由麻省理工学院的以为姓李的女士提出的。

  2. 在所有引用基类的地方必须能透明的使用其子类的对象。

  3. 在使用继承的时候,遵循里式替换原则,在子类中尽量不要重写父类的方法。

demo1-存在的问题

package com.lzhpo.principle.liskov;

/**
 * @author lzhpo
 */
public class Liskov {
    public static void main(String[] args) {
        A a = new A();
        System.out.println("11-3=" +a.func1(11, 3));
        System.out.println("11-8=" +a.func1(1, 8));

        System.out.println("---------------------------");
        // 重写父类的方法,导致出错。
        B b = new B();
        System.out.println("11-3=" +b.func1(11, 3));
        System.out.println("1-8=" +b.func1(1, 8));
        System.out.println("11+3+9=" +b.func2(11, 3));
    }
}

class A {
    public int func1(int num1, int num2) {
        return num1 - num2;
    }
}

class B extends A {

    // 重写父类的方法
    @Override
    public int func1(int a, int b) {
        return a + b;
    }

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

运行结果:

11-3=8
11-8=-7
---------------------------
11-3=14
1-8=9
11+3+9=23

问题:重写父类的方法,导致出错。

demo2-遵循里式替换原则

package com.lzhpo.principle.liskov.improve;

/**
 * @author lzhpo
 */
public class Liskov {
    public static void main(String[] args) {
        A a = new A();
        System.out.println("11-3=" +a.func1(11, 3));
        System.out.println("11-8=" +a.func1(1, 8));

        System.out.println("---------------------------");
        // 重写父类的方法,导致出错。
        B b = new B();
        System.out.println("11+3=" +b.func1(11, 3));
        System.out.println("1+8=" +b.func1(1, 8));
        System.out.println("11+3+9=" +b.func2(11, 3));

        System.out.println("11-3=" +b.func3(11, 3));
    }
}

/**
 * 创建一个更加基础的基类
 */
class Base {

}

class A extends Base {
    public int func1(int num1, int num2) {
        return num1 - num2;
    }
}

class B extends Base {
    // 组合A的对象,仍然可以使用A类的方法
    private A a = new A();

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

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

    // 仍然使用A的方法
    public int func3(int a, int b) {
        return this.a.func1(a, b);
    }
}

运行结果:

11-3=8
11-8=-7
---------------------------
11+3=14
1+8=9
11+3+9=23
11-3=8

5. 开闭原则

【开闭原则】:

对扩展功能是开放的,但是对修改是关闭的,这就是开闭原则。

demo1-存在的问题

package com.lzhpo.principle.ocp;

/**
 * @author lzhpo
 */
public class Ocp {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());

        // 增加一个三角形,发现改动的代码特别多,不遵循开闭原则。
        graphicEditor.drawShape(new Triangle());
    }
}

/**
 * 这是一个用于绘图的类
 */
class GraphicEditor {
    public void drawShape(Shape s) {
        if (s.m_type == 1) {
            drawRectangle(s);
        } else if (s.m_type == 2) {
            drawCircle(s);
        } else if (s.m_type == 3) {
            drawTriangle(s);
        }
    }

    public void drawRectangle(Shape r) {
        System.out.println("绘制矩形");
    }

    public void drawCircle(Shape r) {
        System.out.println("绘制圆形");
    }

    public void drawTriangle(Shape r) {
        System.out.println("绘制三角形");
    }
}

/**
 * Shape基类
 */
class Shape {
    int m_type;
}

class Rectangle extends Shape {
    public Rectangle() {
        super.m_type = 1;
    }
}

class Circle extends Shape {
    public Circle() {
        super.m_type = 2;
    }
}

class Triangle extends Shape {
    public Triangle() {
        super.m_type = 3;
    }
}

运行结果:

绘制矩形
绘制圆形
绘制三角形

问题:增加一个三角形,发现改动的代码特别多,不遵循开闭原则。

demo2-遵循开闭原则

package com.lzhpo.principle.ocp.improve;

/**
 * @author lzhpo
 */
public class Ocp {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());

        graphicEditor.drawShape(new Triangle());

        // 增加一个
        graphicEditor.drawShape(new OtherGraphic());
    }
}

/**
 * 这是一个用于绘图的类
 */
class GraphicEditor {
    public void drawShape(Shape s) {
        s.draw();
    }
}

/**
 * Shape基类
 */
abstract class Shape {
    int m_type;

    // 抽象方法
    public abstract void draw();
}

class Rectangle extends Shape {
    public Rectangle() {
        super.m_type = 1;
    }

    @Override
    public void draw() {
        System.out.println("绘制矩形");
    }
}

class Circle extends Shape {
    public Circle() {
        super.m_type = 2;
    }

    @Override
    public void draw() {
        System.out.println("绘制圆形");
    }
}

class Triangle extends Shape {
    public Triangle() {
        super.m_type = 3;
    }

    @Override
    public void draw() {
        System.out.println("绘制三角形");
    }
}

// 增加一个,发现改动的代码还是少的
class OtherGraphic extends Shape {
    public OtherGraphic() {
        super.m_type = 4;
    }

    @Override
    public void draw() {
        System.out.println("绘制其它图形");
    }
}

运行结果:

绘制矩形
绘制圆形
绘制三角形
绘制其它图形

6. 迪米特原则

【迪米特原则(最小知道原则)】:

  1. 降低类之间的耦合度。
  2. 即一个类对自己依赖的类知道的越少越好。

demo1-存在的问题

package com.lzhpo.principle.demeter;

import java.util.ArrayList;
import java.util.List;

/**
 * 迪米特原则(最小知道原则)
 * <p>
 * 1. 降低类之间的耦合度。
 * 2. 即一个类对自己依赖的类知道的越少越好。
 * </p>
 * @author lzhpo
 */
public class Demeter1 {
    public static void main(String[] args) {
        SchoolManger schoolManger = new SchoolManger();
        schoolManger.printAllEmployee(new CollegeManger());

    }
}

class Employee {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

class CollegeEmployee {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

/**
 * 学院管理类
 */
class CollegeManger {

    /**
     * 返回学院所有员工
     * @return list list
     */
    public List<CollegeEmployee> getAllEmployee() {
        ArrayList<CollegeEmployee> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            CollegeEmployee emp = new CollegeEmployee();
            emp.setId("学院员工Id=" +i);
            list.add(emp);
        }
        return list;
    }
}

/**
 * 学校管理类
 */
class SchoolManger {
    // 返回学校总部员工
    public List<Employee> getAllEmployee() {
        ArrayList<Employee> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Employee emp = new Employee();
            emp.setId("员工总部员工Id=" +i);
            list.add(emp);
        }
        return list;
    }

    // 输出学校总部和员工信息Id
    void printAllEmployee(CollegeManger sub) {

        // 不遵循迪米特原则(最小知道原则)
        List<CollegeEmployee> list1 = sub.getAllEmployee();
        System.out.println("-------------分公司员工------------");
        for (CollegeEmployee e : list1) {
            System.out.println(e.getId());
        }

        List<Employee> list2 = this.getAllEmployee();
        System.out.println("-------------学校总部员工------------");
        for (Employee e : list2) {
            System.out.println(e.getId());
        }
    }
}

demo2-遵循迪米特原则

package com.lzhpo.principle.demeter;

import java.util.ArrayList;
import java.util.List;

/**
 * @author lzhpo
 */
public class Demeter1 {
    public static void main(String[] args) {
        SchoolManger schoolManger = new SchoolManger();
        schoolManger.printAllEmployee(new CollegeManger());

    }
}

class Employee {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

class CollegeEmployee {
    private String id;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

/**
 * 学院管理类
 */
class CollegeManger {

    /**
     * 返回学院所有员工
     * @return list list
     */
    public List<CollegeEmployee> getAllEmployee() {
        ArrayList<CollegeEmployee> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            CollegeEmployee emp = new CollegeEmployee();
            emp.setId("学院员工Id=" +i);
            list.add(emp);
        }
        return list;
    }

    /**
     * 输出学院员工信息
     */
    public void printEmployee() {
        List<CollegeEmployee> list1 = getAllEmployee();
        System.out.println("-------------分公司员工------------");
        for (CollegeEmployee e : list1) {
            System.out.println(e.getId());
        }
    }
}

/**
 * 学校管理类
 */
class SchoolManger {
    // 返回学校总部员工
    public List<Employee> getAllEmployee() {
        ArrayList<Employee> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Employee emp = new Employee();
            emp.setId("员工总部员工Id=" +i);
            list.add(emp);
        }
        return list;
    }

    // 输出学校总部和员工信息Id
    void printAllEmployee(CollegeManger sub) {

        // 不遵循迪米特原则(最小知道原则)
//        List<CollegeEmployee> list1 = sub.getAllEmployee();
//        System.out.println("-------------分公司员工------------");
//        for (CollegeEmployee e : list1) {
//            System.out.println(e.getId());
//        }

        // 改进,遵循迪米特原则
        sub.printEmployee();

        List<Employee> list2 = this.getAllEmployee();
        System.out.println("-------------学校总部员工------------");
        for (Employee e : list2) {
            System.out.println(e.getId());
        }
    }
}

运行结果:

-------------分公司员工------------
学院员工Id=0
学院员工Id=1
学院员工Id=2
学院员工Id=3
学院员工Id=4
学院员工Id=5
学院员工Id=6
学院员工Id=7
学院员工Id=8
学院员工Id=9
-------------学校总部员工------------
员工总部员工Id=0
员工总部员工Id=1
员工总部员工Id=2
员工总部员工Id=3
员工总部员工Id=4

7. 合成复用原则

合成/聚合复用原则是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。

简述为:要尽量使用合成/聚合,尽量不要使用继承。

正文到此结束
本文目录