当前位置: > 财经>正文

设计模式六大原则 理财的六大原则

2023-08-23 22:09:09 互联网 未知 财经

设计模式六大原则

SOLID

设计模式的六大原则有:

Single Responsibility Principle:单一职责原则Open Closed Principle:开闭原则Liskov Substitution Principle:里氏替换原则Law of Demeter:迪米特法则Interface Segregation Principle:接口隔离原则Dependence Inversion Principle:依赖倒置原则

把这六个原则的首字母联合起来( L 算做一个)就是 SOLID (solid,稳定的),其代表的含义就是这六个原则结合使用的好处:建立稳定、灵活、健壮的设计

下面我们来分别看一下这六大设计原则。

单一职责原则(Single Responsibility Principle)

单一职责原则简称 SRP ,顾名思义,就是一个类只负责一个职责。它的定义也很简单:

There should never be more than one reason for a class to change.

这句话很好理解,就是说,不能有多个导致类变更的原因。

那这个原则有什么用呢,它让类的职责更单一。这样的话,每个类只需要负责自己的那部分,类的复杂度就会降低。如果职责划分得很清楚,那么代码维护起来也更加容易。试想如果所有的功能都放在了一个类中,那么这个类就会变得非常臃肿,而且一旦出现bug,要在所有代码中去寻找;更改某一个地方,可能要改变整个代码的结构,想想都非常可怕。当然一般时候,没有人会去这么写的。

当然,这个原则不仅仅适用于类,对于接口和方法也适用,即一个接口/方法,只负责一件事,这样的话,接口就会变得简单,方法中的代码也会更少,易读,便于维护。

事实上,由于一些其他的因素影响,类的单一职责在项目中是很难保证的。通常,接口和方法的单一职责更容易实现。

单一原则的好处:

代码的粒度降低了,类的复杂度降低了。可读性提高了,每个类的职责都很明确,可读性自然更好。可维护性提高了,可读性提高了,一旦出现 bug ,自然更容易找到他问题所在。改动代码所消耗的资源降低了,更改的风险也降低了。 里氏替换原则(Liskov Substitution Principle)

里氏替换原则的定义如下:

Functions that use use pointers or references to base classes must be able to use objects of derived classes without knowing it.

翻译过来就是,所有引用基类的地方必须能透明地使用其子类的对象。

里氏替换原则的意思是,所有基类在的地方,都可以换成子类,程序还可以正常运行。这个原则是与面向对象语言的 继承 特性密切相关的。

这是为什么呢?由于面向对象语言的继承特性,子类拥有父类的所有方法,因此,将基类替换成具体的子类,子类也可以调用父类中的方法(其实是它自己的方法,继承于父类),但是如果要保证完全可以调用,光名称相同不行,还需要满足下面两个条件:

子类中的方法的前置条件必须与超类中被覆写的方法的前置条件相同或更宽松。子类中的方法的后置条件必须与超类中被覆写的方法的后置条件相同或更严格。

这样的话,调用就没有问题了。否则,我在父类中传入一个 List 类型的参数,子类中重写的方法参数却变为 ArrayList ,那客户端使用的时候传入一个 LinkedList 类型的参数,使用父类的时候程序正常运行,但根据 LSP 原则,替换成子类后程序就会出现问题。同理,后置条件也是如此。

是不是很好理解?

但是这个原则并不是这么简单的,语法上是肯定不能有任何问题。但是,同时,功能上也要和替换前相同。

那么这就有些困难了,因为子类重写父类中的方法天经地义,子类中的方法不同于父类中的方法也是很正常的,但是,如果这么随便重写父类中的方法的话,那么肯定会违背 LSP 原则,可以看下面的例子:

首先有一个父类,水果类:

public class Fruit { void introduce() { System.out.println("我是水果父类..."); }}

其次,它有一个子类,苹果类:

public class Apple extends Fruit { @Override void introduce() { System.out.println("我是水果子类——苹果"); }}

客户端代码如下:

public static void main(String[] args) { Fruit fruit = new Fruit(); HashMap map = new HashMap(); fruit.introduce();}

运行结果:

我是水果父类...

那么,如果按照 LSP 原则,所有父类出现的地方都能换成其子类,代码如下:

public static void main(String[] args) { Apple fruit = new Apple(); HashMap map = new HashMap(); fruit.introduce();}

那么运行结果就会变成:

我是水果子类——苹果

与原来的输出不同,程序的功能被改变了,违背了 LSP 原则。

因此,可以看到, LSP 原则最重要的一点就是:避免子类重写父类中已经实现的方法。这就是 LSP 原则的本质。这里由于 Fruit 父类已经实现了 introduce 方法,因此子类应该避免再对其进行重写,如果需要增加个性化,就应该对父类进行扩展,而不是重写,否则也会违背开闭原则。

一般来讲,程序中父类大多是抽象类,因为父类只是一个框架,具体功能还需要子类来实现。因此很少直接去 new 一个父类。而如果出现这种情况,那么就说明父类中实现的代码已经很好了,子类只需要对其进行扩展就会,尽量避免对其已经实现的方法再去重写。

依赖倒置原则(Dependence Inversion Principle)

依赖倒置原则的原始定义是这样的:

High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.

翻译一下,就是下面三句话:

高层模块不应该依赖底层模块,两者都应该依赖其抽象。抽象不应该依赖细节。细节应该依赖抽象。

在Java语言中的表现就是:

模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。接口或抽象类不依赖于实现类。实现类依赖于接口或抽象类。

简而言之,我们要尽可能使用接口或抽象类。也就是“面向接口编程” 或者说 “面向抽象编程” ,也就是说程序中要尽可能使用抽象类或是接口。

可能现在还没有使用抽象的习惯,可以看一个例子:比如我们要写一个人吃苹果的程序,首先创建一个苹果类:

public class Apple { public void eaten() { System.out.println("正在吃苹果..."); }}

然后写人的类:

public class Person { void eat(Apple apple) { apple.eaten(); } }

这样,在客户端中我们就可以这样调用:

Person xiaoMing = new Person();Apple apple = new Apple();xiaoMing.eat(apple);

但是这样就有一个问题,如果我不仅仅想吃苹果,还想吃橘子怎么办,在 Person 类中再加一个函数处理橘子?那么吃别的水果呢??总不能程序中每增加一种水果就要在其中增加一个函数吧。

这时候就需要接口了(抽象类也是可以的)

程序就可以这样更改,增加一个水果接口:

public interface Fruit { void eaten(); }

让苹果类实现该接口:

public class Apple implements Fruit { @Override public void eaten() { System.out.println("正在吃苹果..."); }}

然后将Person类中的函数的参数稍作修改:

public class Person { void eat(Fruit fruit) { fruit.eaten(); }}

这样,客户端代码也稍作修改:

Person xiaoMing = new Person();Fruit apple = new Apple();xiaoMing.eat(apple);

这样改完之后,如果再增加一种新的水果,就不需要改变 Person 类了,方便吧。

那么再回来说我们的依赖倒置原则,依赖就是类与类之间的依赖,主要是函数传参,上面的例子已经很明白地介绍了,参数要尽可能使用抽象类或接口,这就是“高层模块不应该依赖底层模块,两者都应该依赖其抽象” 的解释。那么如果要实现这个,就要求每个实现类都应该尽可能从抽象中派生,这就是上面的 “细节应该依赖抽象”。

简而言之,该原则主要有下面几点要求:

每个类都尽量要有接口或抽象类,或者两者都有变量的表面类型尽量是接口或者抽象类(比如程序中的 Fruit apple = new Apple() Fruit 是表面类型, Apple 是实际类型)任何类都不应该从具体类中派生 接口隔离原则(Interface Segregation Principle)

首先

版权声明: 本站仅提供信息存储空间服务,旨在传递更多信息,不拥有所有权,不承担相关法律责任,不代表本网赞同其观点和对其真实性负责。如因作品内容、版权和其它问题需要同本网联系的,请发送邮件至 举报,一经查实,本站将立刻删除。