【设计模式】常见面向对象设计原则

引言

  • 设计原则是思想上的指导
  • 设计模式是实现上的手段
  • 设计模式是设计原则的具体体现
  • 在实际开发中,很少做到完全遵守,总是在有意无意的违反一些或者部分原则
  • 设计是一种危险的平衡艺术

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

There should never be more than one reason for a class to change.
就一个类而言,应该仅有一个引起它变化的原因(职责)。
也就是说,一个职责对应于一个类。

如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。难点在于如何区分职责、职责的粒度问题
软件设计真正要做的内容,就是发现职责并把那些职责相互分离。

如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,就应该考虑类的职责分离。

单一职责适用于接口、类,同时也适用于方法。

开放-封闭原则(OCP原则)Open-Closed Principle

一个软件实体应当对扩展开放,对修改关闭。需要考虑怎样的设计才能面对需求的改变却可以保持相对稳定,从而使得系统可以在第一个版本以后不断推出新的版本。
面对需求,对程序的改动是通过增加新的代码进行的,而不是更改现有代码。合理地抽象、分离出变化与不变化的部分,为变化的部分预留下可扩展的方式。例如:钩子方法或是动态组合对象等
要完全遵守开闭原则是不可能的,也没这个必要。适当的抽象可以提高系统的灵活性、使其可扩展、可维护;过度抽象,会大大增加系统的复杂程度。

钩子方法:是对于抽象方法或者接口中定义的方法的一个空实现。

在实际中的应用,例如有一个接口,这个接口里有7个方法,而你只想用其中1个方法,那么这时,你可以写一个抽象类实现这个接口,在这个抽象类里将你要用的那个方法设置为abstract,其它方法进行空实现,然后你再继承这个抽象类,就不需要实现其它不用的方法,这就是钩子方法的作用。


钩子方法主要用于不遵守接口隔离规则的接口,即dirty interface.

例子:
在这里插入图片描述
招安之法的关键便是不允许更改现有的秩序,但允许将被招安者纳入现有秩序中,从而扩展了这一秩序。
用面向对象的语言来讲,不允许更改的是系统的抽象层,而允许更改的是系统的实现层。

里氏代换原则 Liskov Substitution Principle

里氏代换原则使得开放-封闭成为可能。

If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
Functions that use pointers or references to base classes must be able to use objects of derives classes without knowing it.

如果对于每一个S类型的对象o1都有一个类型为T的对象o2,使得对于用T定义的所有程序P,当o1替换o2时,P的行为不变,则S是T的子类型。

所有引用基类的地方必须能够透明的使用其子类对象。

也就是说,只要父类出现的地方子类就能够出现,而且替换为子类不会产生任何错误或异常。但是反过来,子类出现的地方,替换为父类就可能出现问题了。

里氏代换原则:子类型(subtype)必须能够替换它们的基(父)类型。(子类可以以父类的身份出现)

由于子类型的可替换性才使得使用父类型的模块在无需修改的情况下就可以扩展。因此是实现开闭原则的前提之一。

依赖倒转(置)原则(DIP)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.

要依赖于抽象,不要依赖于具体。

简单的说,依赖倒转原则要求客户端依赖于抽象耦合。原则表述:

  • 抽象不应当依赖于具体实现;具体实现应当依赖于抽象;
  • 高层模块不应当依赖于低层模块,二者都应该依赖于抽象;
  • 要针对接口编程,不针对实现编程

使用传统过程化程序设计所创建的依赖关系,策略依赖于细节,这是糟糕的,因为
策略受到细节改变的影响。依赖倒置原则使细节和策略都依赖于抽象,抽象的稳定性决定了系统的稳定性。

修电脑得到的启示

强内聚、松耦合
由于PC易插拨的方式,那么不管哪一个出问题,都可以在不影响别的部件的前提下进行修改或替换。

依赖倒转原则
要针对接口编程,不要对实现编程,无论主板、CPU、内存、硬盘都是在针对接口编程,如果针对实现编程,那就会出现换内存需要把主板也换了的尴尬


层次化调用的时候,应该是高层调用“底层所拥有的接口”,这是一典型的误解。
一般高层包含对业务功能的处理和业务策略选择,应该被重用,是高层模块去影响底层的具体实现。
这个底层的接口应该是由高层提出的,然后由底层实现,即底层的接口的所有权在高层模块,是一种所有权的倒置


反面例子:
在这里插入图片描述
耦合太紧密,Light发生变化将影响ToggleSwitch

解决办法一:将Light作成Abstract,然后具体类继承自Light。
在这里插入图片描述
优点:
ToggleSwitch依赖于抽象类Light,具有更高的稳定性,而BulbLight与TubeLight继承自Light,可以根据“开放-封闭”原则进行扩展。只要Light不发生变化,BulbLight与TubeLight的变化就不会波及ToggleSwitch。
缺点:
如果用ToggleSwitch控制一台电视就很困难了。总不能让TV继承自Light吧。

解决方法二:在这里插入图片描述
使用该规则要做到:

  • 每个类尽量都有接口或抽象类
  • 变量的表面类型尽量是接口或者是抽象类
  • 任何类都不应该从具体类派生
  • 结合里氏代换原则使用

关于依赖倒转原则的详细介绍

迪米特法则(LoD)Law of Demeter

又叫最少知识原则(Least Knowledge Principle或简写为LKP)

如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
也就是说,一个对象应当对其它对象有尽可能少的了解。

只与你直接的朋友们通信,不要跟“陌生人”说话

迪米特法则其根本思想,是强调了类之间的松耦合
类之间的耦合越弱,越有利于复用,一个处于弱耦合的类被修改,不会对有关系的类造成波及,信息的隐藏促进了软件的复用。

合成/聚合复用原则

要尽量使用合成/聚合,而不是继承关系达到复用的目的。

合成/聚合原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。

在程序设计中,能采用合成聚合的地方就不要采用继承

为什么不用继承?

  • 继承
    优点:
    实现较为容易
    缺点:
    继承复用破坏包装,因为继承将超类的实现细节暴露给了子类。 因为超类的内部细节常常对子类是透明的,因此这种复用是透明的复用,又叫“白箱”复用。
    如果超类的实现改变了,那么子类的实现也不得不发生改变。因此,当一个基类发生了改变时,这种改变会传导到一级又一级的子类,使得设计师不得不相应的改变这些子类,以适应超类的变化。

  • 合成聚合
    优点:
    这种复用是黑箱复用,因为成分对象的内部细节是新对象看不见的。
    缺点:
    这种复用建造的系统会有较多的对象需要管理。

关于合成聚合复用原则的详细介绍1

关于合成聚合复用原则的详细介绍2

接口隔离原则(ISP)

接口隔离原则是单一职责原则在接口领域应用的特例

接口隔离原则(Interface Segregation Principle)讲的是:使用多个专门的接口比使用单一的总接口要好。换而言之,从一个客户类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小接口上的。
过于臃肿的接口是对接口的污染。不应该强迫客户依赖于它们不用的方法。

实现方法:
每个接口都要符合单一职责原则,同时使用多重继承分离接口,否则则为dirty interface。

总结

单一职责原则

就一个类而言,应该仅有一个引起它变化的原因。

开放-封闭原则

一个软件实体应当对扩展开放,对修改关闭

里氏代换原则

子类型(subtype)必须能够替换它们的基(父)类型

依赖倒转(置)原则

要依赖于抽象,不要依赖于具体

接口隔离原则

使用多个专门的接口比使用单一的总接口总要好

合成/聚合复用原则

要尽量使用合成/聚合,而不是继承关系达到复用的目的

迪米特法则

一个软件实体应当尽可能少的与其他实体发生相互作用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值