xiaobenchi

导航

设计模式之七大设计原则1

设计模式之七大设计原则

​ 在编写软件的过程中,程序员面临着来自耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性

等多方面的挑战。

设计模式常用的七大设计模式:

  1. 单一职责原则

  2. 接口隔离原则

  3. 依赖倒转(倒置原则)

  4. 里氏替换原则

  5. 开闭原则

  6. 迪米特法则

  7. 合成复用原则

    接下来我们就来详细讨论一下这七种设计原则

一、单一职责原则(Single Responsibility Principle )

对类来说,即一个类应该只负责一项职责。

用类实现单一职责

将类分解,根据不同的职责创建不同的类

这种方法会分解类,在复杂场景下应用的比较多。但在简单场景将类分解会加大开销。

用方法实现单一职责

这种方式使得每一个方法对应一种职责,从而实现了单一职责。

public class SingleResponsibility1{
    
    public static void main(String[] args){
        Vehicle2 vheicle2 = new Vehicle2();
        vehicle2.run("汽车");
        vehicle2.runAir("飞机");
        vheicle3.runWater("轮船");
    }
}

class Vehicle2{
    public viod run(String vehicle){
        System.out.println(vehicle + "公路上跑")
    }
    public viod runAir(String vehicle){
        System.out.println(vehicle + "天上飞")
    }
    public viod runWater(String vehicle){
        System.out.println(vehicle + "水上漂")
    }
    
}

单一职责原则注意事项和细节

  1. 降低类的复杂度,一个类只负责一项职责。

  2. 提高类的可读性,可维护性。

  3. 降低变更带来的风险。

  4. 通常情况下,我们应该遵守单一职责原则,只有逻辑足够简单,才能在代码级别违反单一职责原则;

    只有类中方法数量足够少,可以在方法级别上保持单一职责原则。

二、接口隔离原则(Interface Segregation Principle)

基本介绍

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

违反接口隔离

按照隔离原则应该将Interface_1拆分为独立的几个接口。

接口隔离

三、依赖倒转原则(Dependence Inversion Principle)

基本介绍

  1. 高层模块不应该依赖低层模块,二者都应该依赖抽象

  2. 抽象不应该依赖细节,细节应该依赖抽象

  3. 依赖倒转原则的中心思想是面向接口编程

  4. 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比

    以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类.

  5. 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给它们的实现类去完成。

应用实例

  1. 不满足依赖倒转原则实例
public class DependenceInversion1{
    public static void main(String[] args){
        Person Person = new Person();
        person.receive(new Email);
    }
}

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

//完成Person接收消息的功能
class Person{
    public void receive(Email email){
        System.out.println(email.getInfo());
    }
}

代码分析:

简单,易得到

如果我们获取的对象是微信,短信等等,则新增类,同时Person也要新增接收方法

  1. 改进方法

    思路:引入一个抽象的接口IReceiver,表示接收者,这样Person类与接口IReceiver发生依赖,因为Email,WeiXin

    等等属于接收的范围,它们各自实现IReceiver接口就可以,这样设计之后,我们就符合依赖倒转原则。

    代码实现:

    public class DependenceInversion2{
        public static void main(String[] args){
            Person Person = new Person();
            person.receive(new Email);
            person.receive(new WeiXIn);
        }
    }
    
    //定义一个接口
    interface IReceiver{
        public String getInfo();
    }
    
    class Email implement IReceiver{
        public String getInfo(){
            return "电子邮件信息:hello,world";
        }
    }
    
    //增加微信
    class WeiXIn implement IReceiver{
        public String getInfo(){
            return "微信信息:hello,WeiXin";
        }
    }
    
    //完成Person接收消息的功能
    class Person{
        public void receive(IReceiver receiver){
            System.out.println(receiver.getInfo());
        }
    }
    

依赖关系传递的三种方式

  1. 通过接口传递实现依赖
  2. 通过构造方法依赖传递
  3. 通过setter方法传递

四、里氏替换原则(Liskov Substitution Principle)

OO中的继承性的思考和说明

  1. 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是再设定规范和契约,虽然它不强制要

    求所有的子类必须遵守这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系

    造成破坏。

  2. 继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植

    性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须要考虑到

    所有的子类,并且父类修改后,所有涉及到子类的功能都可能产生故障。

  3. 问题提出:在编程中,如何正确使用继承?--> 里氏替换原则

基本介绍

  1. 如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都

    换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型,换句话说,所有引用基类的地方

    必须能够透明地使用其子类的对象。

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

  3. 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依

    赖来解决问题。

案例分析

  1. 不符合里氏替换

    在子类中重写了父类的方法,改变了方法的功能,导致方法调用混乱。

//不符合里氏替换的实现
public class Liskov1{
    public static void main(String[] args){
        
    }
}

//A类
class A{
    //返回两数之差
    public int func1(int num1,int num2){
        return num1-num2;
    }
}

//B类
class B extend A{
    //这里重写了A类的方法
    public int func1(int a, int b){
        return a + b;
    }
    
    public int func2(int a, int b){
        return func1(a + b) + 9;
    }
}
  1. 里氏替换的实现

    创建一个更加基础的的基类,把更加基础的方法和成员写到base类。A类和B类之间通过聚合,组合等方式建立

    联系。B类不再继承A类,所以方法功能的使用不会混淆。

    聚合

    // 符合里氏替换的方式
    public class Liskov2{
        public static void main(String[] args){
            
        }
    }
    
    //base类
    class base{
        
    }
    //A类
    class A extends Base{
        //返回两数之差
        public int func1(int num1,int num2){
            return num1-num2;
        }
    }
    
    //B类
    class B extend base{
    	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;
        }
        
        public int func3(int a,int b){
            return this.a.func1(a,b);
        }
    }
    

五、开闭原则(Open Closed Principle)

基本介绍

  1. 开闭原则是编程中最基础、最重要的设计原则。

  2. 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,

    用实现扩展细节。

  3. 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码实现变化。

  4. 编程中遵守其他的原则,最终都是为了实现开闭原则。开闭原则是核心原则。

案例分析

  1. 普通实现,不考虑开闭原则

    如果要新增一个图形,例如新增一个三角形类,那么就要在GraphicEditor类中进行更改,也就是用户端也

    得修改,违反了开闭原则。

public class OCP1{
    public static void main(String[] args){
    }
}

//绘图类
class GraphicEditor{
    //接受shape对象,然后根据style,来绘制不同的图形
    public void drawShape(Shape s){
        if(s.m_type == 1)
            drawRectangle(s);
        else if (s.m_type == 2)
            drawCircle(s);
    }
    
    public void drawRectangle(Shape r){
        System.out.println("绘制矩形");
    }
    
    public void drawCircle(Shape r){
        System.out.println("绘制圆形");
    }
}

class Shape{
    int m_type;
}

class Rectangle extends Shape{
    Rectangle(){
        super.m_type = 1;
    }
}
class Circle extends Shape{
    Circle(){
        super.m_type = 2;
    }
    
}
  1. 使用开闭原则进行实现

    思路:把创建Shape类做成抽象类,并提供一个抽象的draw方法,让子类去实现即可,这样使得我们有新

    的图形需要扩展的时候,只需要让新的图形类继承Shape,并实现draw方法即可,这样使用方不需要修改

    代码。

public class OCP2{
    public static void main(String[] args){
        GraphicEditor graphicEditor = new GraphicEditor();
        graphicEditor.drawShape(new Circle());
    }
}

//绘图类
class GraphicEditor{
    //接受shape对象,然后根据style,来绘制不同的图形
    public void drawShape(Shape s){
        s.draw();
    }
    
}
// 基类
abstract class Shape{
    int m_type;
    public abstract draw();//抽象方法
}

class Rectangle extends Shape{
    Rectangle(){
        super.m_type = 1;
    }
    public void draw(){
        System.out.println("绘制矩形");
    }
}
class Circle extends Shape{
    Circle(){
        super.m_type = 2;
    }
     public void draw(){
        System.out.println("绘制圆形");
    }
}

六、迪米特法则(Demeter Principle)

基本介绍

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

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

  3. 迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多

    么复杂,都尽量将逻辑封装在类的内部。对外部除了提供的public方法,不对外泄露任何信息。

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

  5. 直接的朋友:每个对象都会与其他的对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间

    是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现在成员变量,方法参数,方法

    返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部

    变量的形式出现在类的内部。

实现方法

​ 将一些实现方法放入本身的类内部,而不要在本身的类内对另一个类的对象进行方法的构造。

迪米特法则的注意事项

  1. 迪米特法则的核心思想是降低类之间的耦合。
  2. 降低耦合并不意味着没有耦合,依赖关系是会存在的。

七、合成复用原则(Composite Reuse Principle)

基本介绍

原则上是尽量少的去使用继承,而是采用聚合、组合等方式,从而降低了耦合度。

实现方法

合成复用

小结

设计原则的核心思想:

  1. 找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混合在一起。
  2. 针对接口编程,而不是针对具体实现编程。
  3. 为了交互对象之间的松耦合设计而努力。

posted on 2022-04-01 19:11  小迟在努力  阅读(41)  评论(0)    收藏  举报