Welcome everyone

设计模式的七大原则

设计模式 汪明鑫 1341浏览 0评论

设计模式七大原则是23种设计模式依据的原则,分别为

单一职责原则、接口隔离原则、依赖倒转原则、里氏替换原则、开闭原则、迪米特法则、合成复用原则

 

 

单一职责原则

【Single Responsibility Principle】

对类来说的,即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。

当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2。

 

降低类的复杂性,一个类只负责一项责任

提高类的可读性、可维护性

降低变更带来的风险

 

通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违

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

一个类的方法多了还是要根据功能分解成多个类

 

下面有一个类图,封装的是关于User的属性和操作

 

根据单一职责原则,拆分类

一个负责用户属性,一个负责用户行为

当操作用户属性的需求变更是不会对用户行为造成影响,也就是说降低了变更带来的风险

 

实际的使用中,我们更倾向于使用两个不同的类或接口,一个就是 IUserBO, 一个是 IUserBiz

 

 

 

在JDBC中也是根据不同职责划分接口

java.sql.Connection:Creates a Statement object for sending SQL statements to the database.

java.sql.Statement:Executes the given SQL statement, which returns a single ResultSet object.

 

单一职责原则,难点在于职责的划分,在实际中,我们尽量根据项目需求的不同角度去划分职责。

切记规矩是死的人是活的。

 

接口隔离原则

Interface Segregation Principle

 

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

 

Clients should not be forced to depend upon interfaces that they don’t use.

 

The dependency of one class to another one should depend on the smallest possible

interface.

 

 

依赖它需要的接口,客户端需要什么接口就提

供什么借口,把不需要的接口剔除掉,那就需要对接口进行细化,保证其纯洁性。

 

建立单一接口,不要建立臃肿庞大的接口。

接口尽量细化,同时接口中的方法尽量的少。

 

 

举个例子:

 

 

代码 version1:

public interface IPettyGirl {

    //颜值高
    void goodLooking();

    //身材好
    void niceFigure();

    //气质好
    void greatTemperament();

}

 

public class PettyGirl implements IPettyGirl {

    private String name;

    public PettyGirl(String name) {
        this.name = name;
    }

    @Override
    public void goodLooking() {
        System.out.println(this.name+"长得真好看");
    }

    @Override
    public void niceFigure() {
        System.out.println(this.name+"身材好");
    }

    @Override
    public void greatTemperament() {
        System.out.println(this.name+"女神气质");
    }
}
public abstract class AbstractSearcher {

    IPettyGirl pettyGirl;

    public AbstractSearcher(IPettyGirl pettyGirl) {
        this.pettyGirl = pettyGirl;
    }

    public abstract void show();
}
public class Searcher extends AbstractSearcher {

    public Searcher(IPettyGirl pettyGirl) {
        super(pettyGirl);
    }

    @Override
    public void show() {
        super.pettyGirl.goodLooking();
        super.pettyGirl.niceFigure();
        super.pettyGirl.greatTemperament();
    }
}
public class Client {
    public static void main(String[] args){

      PettyGirl baojie = new PettyGirl("宝姐");

      Searcher searcher = new Searcher(baojie);
      searcher.show();

    }
}

 

代码 version2 :

IPettyGirl 拆成2接口 IGoodBodyGirl 和  IGreatTemperamentGirl

public interface IGoodBodyGirl {
    //颜值高
    void goodLooking();

    //身材好
    void niceFigure();

}
public interface IGreatTemperamentGirl {
    //气质好
    void greatTemperament();
}
public class PettyGirl implements IGoodBodyGirl,IGreatTemperamentGirl {

    private String name;

    public PettyGirl(String name) {
        this.name = name;
    }

    @Override
    public void goodLooking() {
        System.out.println(this.name+"长得真好看");
    }

    @Override
    public void niceFigure() {
        System.out.println(this.name+"身材好");
    }

    @Override
    public void greatTemperament() {
        System.out.println(this.name+"女神气质");
    }
}
public abstract class AbstractSearcher {

    IGoodBodyGirl goodBodyGirl;

    IGreatTemperamentGirl greatTemperamentGirl;

    public AbstractSearcher(IGoodBodyGirl goodBodyGirl) {
        this.goodBodyGirl = goodBodyGirl;
    }

    public AbstractSearcher(IGreatTemperamentGirl greatTemperamentGirl) {
        this.greatTemperamentGirl = greatTemperamentGirl;
    }

    public abstract void show();
}
public class GoodBodyGirlSearcher extends  AbstractSearcher {
    public GoodBodyGirlSearcher(IGoodBodyGirl goodBodyGirl) {
        super(goodBodyGirl);
    }

    @Override
    public void show() {
        super.goodBodyGirl.goodLooking();
        super.goodBodyGirl.niceFigure();
    }
}
public class GreatTemperamentSearcher extends AbstractSearcher {

    public GreatTemperamentSearcher(IGreatTemperamentGirl greatTemperamentGirl) {
        super(greatTemperamentGirl);
    }

    @Override
    public void show() {
        super.greatTemperamentGirl.greatTemperament();
    }
}
public class Client {
    public static void main(String[] args){
        PettyGirl baojie = new PettyGirl("宝姐");

        GoodBodyGirlSearcher searcher =new GoodBodyGirlSearcher(baojie);
        searcher.show();

        GreatTemperamentSearcher searcher1 = new GreatTemperamentSearcher(baojie);
        searcher1.show();

    }
}

 

 

贯彻使用接口隔离原则最

好的方法就是一个接口一个方法,保证绝对符合接口隔离原则(有可能不符合单一职责原则),但你会采用

吗?!不会,除非你是疯子!那怎么才能正确的使用接口隔离原则呢? 答案是根据经验和常识决定接口的

粒度大小,接口粒度太小,导致接口数据剧增,开发人员呛死在接口的海洋里;接口粒度太大,灵活性降

低,无法提供定制服务,给整体项目带来无法预计的风险。

 

接口尽量小但要有限度。

 

 

 

依赖倒转原则

Dependence Inversion Principle

 

通过面向接口编程,我们的代码就有了很高的扩展性,降低了代码之间的耦合度,提高了系统的稳定性。

 

这个概念比较抽象,

直接上例子,人接收消息

 

代码version1:

public class Main {
    public static void main(String[] args){
        Person zhangsan = new Person();
        zhangsan.recieveMsg(new Email());
    }
}


class Email{
    public String getInfo(){
        return "nmsl";
    }
}

class Person{

    //recieveMsg直接依赖了一个类!!! 显然不行
    //代码耦合度太强  如果更改获取的消息(微信、短信),还需要新增类,以及需要新加接收消息的重载方法
    //引入一个抽象的接口  IReceiver  依赖抽象 体现接口编程 这样设计的代码更灵活
    public void recieveMsg(Email email){
        System.out.println(email.getInfo());
    }
}

 

version2:

面向接口编程

public class Main {
    public static void main(String[] args){
        Person lisi = new Person();

        IReceiver receiver = new Email();
        lisi.recieveMsg(receiver);

        receiver = new WeChat();
        lisi.recieveMsg(receiver);
    }
}

//定义一个接口  面向接口编程
interface  IReceiver{
    String getInfo();
}

class Email implements IReceiver{
    public String getInfo(){
        return "电子邮箱:nmsl";
    }
}

class WeChat implements IReceiver{
    public String getInfo(){
        return "微信:mmp";
    }
}

class Person{
    public void recieveMsg(IReceiver receiver){
        System.out.println(receiver.getInfo());
    }
}

 

依赖关系传递的三种方式

  • 接口传递
  • 构造方法传递
  •  setter方式传递

 

底层模块尽量要有抽象类或接口。

 

里氏替换原则

Liskov Substitution Principle

 

关于继承

继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契

约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实

现的方法任意修改,就会对整个继承体系造成破坏。

继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵

入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,

则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子

类的功能都有可能产生故障

 

 

因此使用继承需要遵守一定的原则 —–> 里氏替换原则

 

子类可以去扩展父类的功能,但是不能改变父类原有的功能。

 

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

 

第三条和第四条比较难理解=-=

 

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

P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1

的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。

 

继承实际上让两个类耦合性增强了,在适当的情况下,可 以通过聚合,组合,依赖 来解决问题。.

 

确保程序遵循里氏替换原则可以要求我们的程序建立抽象,通过抽象去建立规范,然后用实现去扩展细节

 

 

如果B继承A,而且要重写A的所有方法,那你都重写为啥还要重新继承,

此时可以把B提到和A平级,抽取一个Base类,让A和B都去继承Base类,

自己写自己的方法,降低耦合性。

 

demo

调用时就可能会出错

 

断掉A和B的继承关系,抽出共有类Base

使用组和的关系,让B持有A的对象就可以调用A的方法

 

 

 

开闭原则

Open Close Principle

 

开闭原则的定义是软件中的对象(类,模块,函数等)应该对于扩展是开放的(对提供方而言),但是对于修改是关闭的(对使用方而言)

当需求发生改变的时候,我们需要对代码进行修改,这个时候我们应该尽量去扩展原来的代码,而不是去修改原来的代码,因为这样可能会引起更多的问题。

这个准则和单一职责原则一样,是一个大家都这样去认为但是又没规定具体该如何去做的一种原则。

开闭原则我们可以用一种方式来确保他,我们用抽象去构建框架,用实现扩展细节。这样当发生修改的时候,我们就直接用抽象了派生一个具体类去实现修改。

 

编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则

因此开闭原则是核心!!!

 

举个画图的例子:

public class OpenClosePrinciple{

	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 {
	//接收Shape对象,调用draw方法
	public void drawShape(Shape s) {
		s.draw();
	}

	
}

//Shape 基类
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 OtherGraphic extends Shape {
	
	@Override
	public void draw() {
		System.out.println(" 绘制其它图形 ");
	}
}

需要什么图形,就直接扩展,完全不需要修改使用方的代码

 

 

迪米特法则

Low Of Demeter

 

1) 一个对象应该对其他对象保持最少的了解  —-> 降低耦合

2) 类与类关系越密切,耦合度越大 —-> 关系松散

3) 迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的

越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内

部。对外除了提供的public 方法,不对外泄露任何信息

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

5) 直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,

我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合

等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而

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

的形式出现在类的内部。

 

 

代码表示直接朋友:

/**
 * @author: wang ming xin
 * @create: 2019-08-06 23:38
 */
public class A {
}


class B{
    A a;  //A是B的直接朋友

    public void doSomething(A a){  //A是B的直接朋友
        //....
    }

    public A getA(){

        //....
        A a = new A();
        //....

        return a;  //A是B的直接朋友

    }

}

class C{
    public void doSomething(){
        //....
        A a = new A(); //A不是C的直接朋友,但是C直接在方法中使用,违反了【迪米特法则】
                       //即陌生的类不要以局部变量的形式出现在类的内部
    }
}

 

如果出现陌生类,可以考虑把对陌生类的操作封装在直接朋友的类中。

通过直接朋友间接的操作陌生类。

 

 

合成复用原则

【Composite Reuse Principle】

 

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

 

 

 

空心箭头:聚合

实心箭头:组合

 

 

设计模式原则的核心思想

  • 抽取公共部分。
  • 面向接口编程。
  • 对象之间的低耦合。

 

转载请注明:汪明鑫的个人博客 » 设计模式的七大原则

喜欢 (0)

说点什么

您将是第一位评论人!

提醒
avatar
wpDiscuz