设计模式的七大原则

 


编写软件过程中,程序员面临着来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性 等多方面的挑战, 设计模式是为了让程序(软件),具有如下更好的特性

  1. 代码重用性 (即:相同功能的代码,不用多次编写)
  2. 可读性 (即:编程规范性,便于其他程序员的阅读和理解)
  3. 可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
  4. 可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)

使程序呈现高内聚, 低耦合的特性

那么一个设计模式在这样设计的时候,要遵循哪些原则呢,即设计模式为什么这么设计的依据, 下面开始介绍,设计模式的 七大原则

  1. 单一职责原则
  2. 接口隔离原则
  3. 依赖倒转(倒置)原则
  4. 里氏替换原则
  5. 开闭原则
  6. 迪米特法则
  7. 合成复用原则

开闭原则

对扩展开放,对修改关闭,不能去修改原有的代码,实现一个热插拔的效果,易于维护和升级,

想要达到这种效果,我们需要使用接口和抽象类

创建抽象类

1
2
3
4
public abstract class AbstratSkin {
 
    public abstract void display();
}

 

1
2
3
4
5
6
7
public class DefaultSkin extends AbstratSkin {
 
    @Override
    public void display() {
        System.out.println("默认颜色");
    }
}

  

1
2
3
4
5
6
public class HeiSkin extends AbstratSkin{
    @Override
    public void display() {
        System.out.println("黑色");
    }
}

  可以理解为具体业务,去执行程序的

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Input {
 
    private AbstratSkin skin;
 
    public void setSkin(AbstratSkin skin) {
        this.skin = skin;
    }
 
    public void display() {
        skin.display();
    }
 
}

  

1
2
3
4
5
6
7
8
9
public class Client {
 
    public static void main(String[] args) {
        Input input = new Input();
        DefaultSkin defaultSkin = new DefaultSkin();
        input.setSkin(defaultSkin);
        input.display();
    }
}

  

 里式替换原则:

是面向对象设计的基本原则之一.

任何基类可以出现的地方,子类一定可以出现,通俗理解:子类可以拓展父类的功能,但不能改变父类原有的功能,换句话说,子类继承父类时,除了添加新的方法完成新增功能外,尽量不要重写父类的方法.

如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特比偶尔是运用多态比较频繁时,程序运行出错的概率会非常大.

面向对象中的继承性思考:

  1. 继承包含这样一层含义:父类中凡是已经实现好的方法, 实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
  2. 继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障
  3. 问题提出:在编程中,如何正确的使用继承? => 里氏替换原则

里氏替换原则基本介绍:

  1. 里氏替换原则(Liskov Substitution Principle)在1988年,由麻省理工学院的以为姓里的女士提出的。
  2. 如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。
  3. 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
  4. 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了, 在适当的情况下,可以通过聚合,组合,依赖 来解决问题

我们也可以通过提升的方法,来尽量满足里氏替换原则,假设现在有两个类,A 类和 B 类,如果 B 类继承 A 类,需要重写 A 类中的某些方法,那么,我们在 A 类 和 B 类之上,再抽取出一个更加通用的父类 Base,让 A 类和 B 类同时去继承 Base,这样 B 类就无须重写 A 类中的某些方法,达到基类的引用对子类对象透明的效果

代码一:

未遵循里氏替换原则,由于子类 B 继承父类 A 时重写了 func1() 方法,导致程序中使用多态时,本意是想调用重写前的方法,结果变成了重写后的方法,所以程序输出结果和预期不同

public interface Quadrilateral {

    double getLength();

    double getWidth();
}

 

复制代码
public class Rectangle implements Quadrilateral{

    private double length;

    private double width;

    public void setLength(double length) {
        this.length = length;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    @Override
    public double getLength() {
        return length;
    }

    @Override
    public double getWidth() {
        return width;
    }
}
复制代码
复制代码
public class Square implements Quadrilateral {

    private double side;

    public void setSide(double side) {
        this.side = side;
    }

    @Override
    public double getLength() {
        return side;
    }

    @Override
    public double getWidth() {
        return side;
    }
}
复制代码
复制代码
public class Client {

    public static void main(String[] args) {
        Rectangle r = new Rectangle();
        r.setLength(20);
        r.setWidth(10);
        resize(r);
        print(r);
    }

    public static void resize(Rectangle r) {
        while (r.getWidth() <= r.getLength()) {
            r.setWidth(r.getWidth() + 1);
        }
    }

    public static void print(Quadrilateral q) {
        System.out.println(q.getLength());
        System.out.println(q.getWidth());
    }
}
复制代码

依赖倒转原则:

依赖倒转(Dependence Inversion Principle )原则是指:

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

    /**
     * 存储数据
     * @param data
     */
    void save(String data);

    /**
     * 获取数据
     * @return
     */
    String getData();
}
复制代码

 

public interface Cpu {

    /**
     * 运行cpu
     */
    void run();
}
public interface Memory {

    /**
     * 保存
     */
    void save();
}
创建实现类实现业务
复制代码
public class XiJieHardDisk implements HardDisk{

    @Override
    public void save(String data) {
        System.out.println("使用希捷硬盘存储数据为:" + data);
    }

    @Override
    public String getData() {
        System.out.println("使用希捷硬盘读取数据");
        return "数据";
    }
}
复制代码
public class IntelCpu implements Cpu{
    @Override
    public void run() {
        System.out.println("使用inter处理器");
    }
}
public class JinstonMemory implements Memory{

    @Override
    public void save() {
        System.out.println("使用金士顿内存条");
    }
}

组装接口即可

复制代码
@Getter
@Setter
public class Computer {

    private HardDisk disk;

    private Cpu cpu;

    private Memory memory;

    public void run() {
        System.out.println("运行计算机");
        String data = disk.getData();
        System.out.println("从硬盘上读取的数据是:" + data);
        cpu.run();
        memory.save();
    }

}
复制代码
复制代码
public class ComputerDemo {

    public static void main(String[] args) {
        //创建组件
        HardDisk hardDisk = new XiJieHardDisk();
        Cpu cpu = new IntelCpu();
        Memory memory = new JinstonMemory();
        //创建计算机
        Computer computer = new Computer();
        //组装计算机
        computer.setDisk(hardDisk);
        computer.setCpu(cpu);
        computer.setMemory(memory);
        computer.run();
    }

}
复制代码

 

 如果后续要替换硬件,可以创建好新的硬件实现接口功能,然后在Computer替换变量即可

接口隔离原则:

Interface Segregation Principle

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

举例说明:

  1. 类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口Interface1对于类A和类C来说不是最小接口,但是类B和类D必须去实现他们不需要的方法。
  2. 按隔离原则应当这样处理:将接口Interface1拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则

 

 

 问题代码:

复制代码
public interface SafetyDoor {

    //防盗
    void antiTheft();

    //防火
    void fireProof();

    //防水
    void waterProof();
}

//实现功能
public class HeiSafetyDoor implements SafetyDoor{
    @Override
    public void antiTheft() {
        System.out.println("防盗");
    }

    @Override
    public void fireProof() {
        System.out.println("防火");
    }

    @Override
    public void waterProof() {
        System.out.println("防水");
    }
}

//程序开始,开始使用安全门
public class Client {

    public static void main(String[] args) {
        HeiSafetyDoor door = new HeiSafetyDoor();
        door.antiTheft();
        door.fireProof();
        door.waterProof();
    }
}
复制代码

 

 

 此时:如果我另一个类,不想实现其中的功能,就不得不实现了,所以要把接口隔离开

优化代码:

复制代码
//防盗接口
public interface AntiTheft {

    //防盗
    void antiTheft();
}

//防火接口
public interface FireProof {

    //防火
    void fireProof();

}
//防水接口
public interface WaterProof {

    //防水
    void waterProof();

}

//实现我需要的功能接口即可

public class HeiSafetyDoor implements AntiTheft,FireProof,WaterProof{

    @Override
    public void antiTheft() {
        System.out.println("防盗");
    }

    @Override
    public void fireProof() {
        System.out.println("防火");
    }

    @Override
    public void waterProof() {
        System.out.println("防水");
    }
}

//这个实现类我只需要两个功能,那我就不需要实现别的接口就好了

public class RedSafetyDoor implements AntiTheft,FireProof{
    @Override
    public void antiTheft() {
        System.out.println("防盗");
    }

    @Override
    public void fireProof() {
        System.out.println("防火");
    }
}

//程序启动,开始使用安全门

public class Client {

    public static void main(String[] args) {
        HeiSafetyDoor door = new HeiSafetyDoor();
        door.antiTheft();
        door.fireProof();
        door.waterProof();

        System.out.println("============");
        //创建另一个安全门对象
        RedSafetyDoor redSafetyDoor = new RedSafetyDoor();
        redSafetyDoor.antiTheft();
        redSafetyDoor.fireProof();

    }
}
复制代码

迪米特法则

迪米特法则的基本介绍

  1. 一个对象应该对其他对象保持最少的了解

  2. 类与类关系越密切,耦合度越大

  3. 迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public 方法,不对外泄露任何信息

  4. 迪米特法则还有个更简单的定义:只与直接的朋友通信 

 

复制代码
//明星
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Star {

    private String name;
}

//粉丝
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Fans {

    private String name;
}

//公司
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Company {
    private String name;

}

@Data
public class Agent {

    private Star star;

    private Fans fans;

    private Company company;

    //和粉丝见面
    public void meeting() {
        System.out.println(star.getName() + "和" + fans.getName() + "见面");
    }

    //和公司洽谈
    public void business() {
        System.out.println(star.getName() + "和" + company.getName() + "洽谈");
    }
}

public class Client {

    public static void main(String[] args) {
        //创建经纪人
        Agent agent = new Agent();
        //创建明星
        Star star = new Star("林青霞");
        //创建粉丝
        Fans fans = new Fans("李四");
        //创建公司
        Company company = new Company("苹果");
        agent.setStar(star);
        agent.setFans(fans);
        agent.setCompany(company);
        agent.meeting();
        agent.business();
    }
}
复制代码

合成复用原则

合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承,即尽量使用 has a 的关系,而不要使用 is a 的关系

 
posted @   着迷JAVA  阅读(74)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示