设计模式之七大原则

记忆方法:开接一单里迪合。

一、开闭原则

开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

1、反面Demo

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Demo {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        // 新增绘制三角形
        graphicEditor.drawShape(new Triangle());
    }
}
// 图形基类
class Shape {
    int type;
}
// 继承Shape的矩形类
class Rectangle extends Shape {
    Rectangle() {super.type = 1;}
}
// 继承Shape的圆形类
class Circle extends Shape {
    Circle() {super.type = 2;}
}
// 新增三角形类,继承Shape类
class Triangle extends Shape {
    Triangle() {super.type = 3;}
}
// 绘图类
class GraphicEditor {
    public void drawShape(Shape shape) {
        if (shape.type == 1) drawRectangle();
        if (shape.type == 2) drawCircle();
        // 新增三角形绘制
        if (shape.type == 3) drawTriangle();
    }
    private void drawRectangle() {System.out.println("绘制矩形");}
    private void drawCircle() {System.out.println("绘制圆形");}
    // 新增绘制三角形方法
    private void drawTriangle() {System.out.println("绘制三角形");}
}

2、正面Demo

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Demo {
    public static void main(String[] args) {
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Rectangle());
        graphicEditor.drawShape(new Circle());
        // 新增绘制三角形
        graphicEditor.drawShape(new Triangle());
    }
}
// 图形基类,抽象类
abstract class Shape {
    public abstract void draw();
}
// 矩形类
class Rectangle extends Shape {
    @Override
    public void draw() {System.out.println("绘制矩形");}
}
// 圆形类
class Circle extends Shape {
    @Override
    public void draw() {System.out.println("绘制圆形");}
}
// 三角形类
class Triangle extends Shape {
    @Override
    public void draw() {System.out.println("绘制三角形");}
}
// 绘图类
class GraphicEditor {
    public void drawShape(Shape shape) {
        shape.draw();
    }
}

二、接口隔离原则

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

1、反面Demo

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
interface Interface1 {
    void method1();
    void method2();
    void method3();
    void method4();
    void method5();
}
class A implements Interface1{
    @Override
    public void method1() {
        System.out.println("用到method1");
    }
    @Override
    public void method2() {
        System.out.println("用到method2");
    }
    @Override
    public void method3() {
        System.out.println("用到method3");
    }
    @Override
    public void method4() {}
    @Override
    public void method5() {}
}
class B implements Interface1{
    @Override
    public void method1() {
        System.out.println("用到method1");
    }
    @Override
    public void method2() {}
    @Override
    public void method3() {}
    @Override
    public void method4() {
        System.out.println("用到method4");
    }
    @Override
    public void method5() {
        System.out.println("用到method5");
    }
}
class Test{
    public static void main(String[] args) {
        Interface1 a = new A();
        Interface1 b = new B();
        //A用到了1,2,3
        a.method1();
        a.method2();
        a.method3();
        //B用到了1,4,5
        b.method1();
        b.method4();
        b.method5();
    }
}

2、正面Demo

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
interface Interface1 {
    void method1();
}
interface Interface2 {
    void method2();
    void method3();
}
interface Interface3 {
    void method4();
    void method5();
}
class A implements Interface1,Interface2{
    @Override
    public void method1() {
        System.out.println("用到method1");
    }
    @Override
    public void method2() {
        System.out.println("用到method2");
    }
    @Override
    public void method3() {
        System.out.println("用到method3");
    }
}
class B implements Interface1,Interface3{
    @Override
    public void method1() {
        System.out.println("用到method1");
    }
    @Override
    public void method4() {
        System.out.println("用到method4");
    }
    @Override
    public void method5() {
        System.out.println("用到method5");
    }
}
class Test{
    public static void main(String[] args) {
        A a = new A();
        B b = new B();
        //A用到了1,2,3
        a.method1();
        a.method2();
        a.method3();
        //B用到了1,4,5
        b.method1();
        b.method4();
        b.method5();
    }
}

三、依赖倒转原则

这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

1、反例Demo

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public class Test {
    public static void main(String[] args) {
        People people=new People();
        people.bug(new Qingcai());
    }
}
class People {
    public void bug(Qingcai qingcai){
        qingcai.run();
    }
}
class Qingcai {
    public void run(){
        System.out.println("买到了青菜");
    }
}

2、正例Demo

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Test {
    public static void main(String[] args) {
        People people=new People();
        people.buy(new Qingcai());
        people.buy(new Luobo());
    }
}
interface Shucai {
    void run();
}
class People {
    public void buy(Shucai shucai){
        shucai.run();
    }
}
class Qingcai implements Shucai{
    @Override
    public void run(){
        System.out.println("买到了青菜");
    }
}
class Luobo implements Shucai {
    @Override
    public void run() {
        System.out.println("买到了萝卜");
    }
}

四、单一职责原则

单一职责原则的基本思想是各司其职,即每个模块,类或者方法只承担单个职责,避免多个职责交叉,从而导致修改其中一个职责的时候,影响另外一个职责。

1、优点

  • 降低类的复杂度,一个类只负责一项职责。
  • 提高类的可读性,可维护性。
  • 降低变更引起的风险。

通常情况下,我们应该遵守单一职责原则,只有逻辑足够简单,才可以在代码级别违反单一职责原则:只有当类中方法数量足够少,可以在方法级别保持单一职责原则。

2、反面Demo

01
02
03
04
05
06
07
08
09
10
11
12
13
public class SingleResponsibilityNegative {
    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+"在公路上跑...");
    }
}

3、正面Demo

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public class SingleResponsibilityPositive {
    public static void main(String[] args) {
        Vehicle vehicle = new Vehicle();
        vehicle.run("汽车");
        vehicle.runWater("轮船");
        vehicle.runAir("飞机");
    }
}
class Vehicle{
    public void run(String vehicle){
        System.out.println(vehicle+"在公路上跑...");
    }
    public void runWater(String vehicle){
        System.out.println(vehicle+"在水上上跑...");
    }
    public void runAir(String vehicle){
        System.out.println(vehicle+"在天上上飞...");
    }
}

五、里氏代换原则

里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

1、反面Demo

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
public class Test {
    public static void main(String[] args) {
        A a = new A();
        a.run();
        System.out.println("将子类替换成父类:");
        B b = new B();
        b.run();
    }
}
class A {
    public void run(){
        System.out.println("父类执行");
    }
}
class B extends A {
    public void run(){
        System.out.println("子类执行");
    }
}
#注:我每次使用子类替换父类的时候,还要担心这个子类有没有可能导致问题。此处子类不能直接替换成父类,故没有遵循里氏替换原则。

2、正面Demo1

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public class Test {
    public static void main(String[] args) {
        A a = new A();
        a.run();
        System.out.println("将子类替换成父类:");
        B b = new B();
        b.run();
        b.runOwn();
    }
}
class A {
    public void run(){
        System.out.println("父类执行");
    }
}
class B extends A {
    public void runOwn(){
        System.out.println("子类执行");
    }
}

3、正面Demo2

当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。我们可以发现他并不是重写,而是方法重载,因为参数不一样,所以他其实是对继承的规范化,为了更好的使用继承。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.HashMap;
import java.util.Map;
public class Test {
    public static void main(String[] args) {
        A a = new A();
        a.run(new HashMap());
        System.out.println("将子类替换成父类:");
        B b = new B();
        b.run(new HashMap());
    }
}
class A {
    public void run(HashMap hashMap){
        System.out.println("父类执行");
    }
}
class B extends A {
    public void run(Map map){
        System.out.println("子类执行");
    }
}

六、迪米特法则,又称最少知道原则

最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。

1、反面Demo

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
class Computers{
    public  void  closeFile(){
        System.out.println("关闭文件");
    }
    public  void  closeScreen(){
        System.out.println("关闭屏幕");
    }
    public  void  powerOff(){
        System.out.println("断电");
    }
}
class Person{
    private Computers computers;
    public void offComputers(){
        computers.closeFile();
        computers.closeScreen();
        computers.powerOff();
    }
}

2、正面Demo

当用户关闭电脑时,需要调用计算机的各个方法,但是这些方法的细节太多了,会出现用户流程出错,遗漏调用等等,对于用户来言,他只需要知道关机按钮就够了。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
class Computers{
    public  void  closeFile(){
        System.out.println("关闭文件");
    }
    public  void  closeScreen(){
        System.out.println("关闭屏幕");
    }
    public  void  powerOff(){
        System.out.println("断电");
    }
    public void offComputers(){
        closeFile();
        closeScreen();
        powerOff();
    }
}
class Person{
    private Computers computers;
    public void offComputers(){
        computers.offComputers();
    }
}

七、合成复用原则

合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。

发表评论

欢迎阅读『设计模式之七大原则|Java、设计模式|Nick Tan-梓潼Blog』