面向对象设计原则

用户头像
leo
关注
发布于: 2020 年 09 月 27 日
面向对象设计原则



面向对象设计(OOD:Object Oriented Design)的六大设计原则





前五个设计原则就是通常所说的SOLID(上方表格缩写的首字母,从上到下)



开闭原则(Open Close Principle)



定义



📌 Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.



即:一个软件实体如类、模块和函数等应该对扩展开放,对修改关闭。



不需要修改软件实体(类、模块、函数等),就应该能实现功能的扩展



优点



可以在不改动原有代码的前提下给程序扩展功能。增加了程序的可扩展性,同时也降低了程序的维护成本。



实现的方法



关键是抽象,通过实现预先设置好的抽象的接口(抽象类)来实现新的功能。



策略模式(Strategy Pattern)

定义



该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。

优点



  1. 使用策略模式可以避免使用多重条件语句

  2. 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码

  3. 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的

  4. 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法

  5. 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离

缺点



  1. 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类

  2. 策略模式造成很多的策略类

实现

继承实现



定义一个抽象类,强制子类实现该方法



这种实现,对父类对子类不一定是透明的,如果父类提供了默认的实现,子类需要了解实现的细节,再决定是否重写



public abstract class Action {
// 也可以将该方法设置成抽象方法, 强迫子类来实现该方法
public void execute() {
// 提供一个默认的实现
}
}
public class ActionModelA extends Action {
public void execute() {
aStyleBrake();// A 风格的行为
}
}
public class ActionModelB extends Action {
public void execute() {
bStyleBrake(); // B 风格的行为
}
}



组合实现



这种实现是透明的,只需要改变对象的引用就可以改变其行为。



public interface IBrakeBehavior {
void execute();
}
public class AStyleBrake implements IBrakeBehavior {
public void execute() {
aStyleBrake(); // A 风格的行为
}
}
public class BStyleBrake implements IBrakeBehavior {
public void execute() {
bStyleBrake(); // B 风格的行为
}
}
public class Action {
protected IBrakeBehavior brakeBehavior;
public void execute() {
brakeBehavior.execute();
}
public void setBrakeBehavior(final IBrakeBehavior brakeType) {
this.brakeBehavior = brakeType;
}
}



最后通过工厂类,根据运行的参数选择出对应的实现



Action action  = ActionFactory.createAction(String parameters);  
action.execute();



只有在策略选择里有条件选择语句,其他地方不出现。

如上述的createAction()方法



适配器模式(Adapter Pattern)

定义



将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。分为两种模式:



  • 类适配器模式:适配器与适配者之间是继承(或实现)关系

  • 对象适配器模式:适配器与适配者之间是关联关系

前者的耦合度比后者高,且要求开发了解现有组件库中的相关组件的内部结构,应用相对较少些。

优点



  • 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。

  • 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。

  • 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

类适配器模式优点:



由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。



对象适配器模式优点:



同一个适配器可以把适配者类和它的子类都适配到目标接口。



缺点



类适配器模式缺点:



对于 Java 等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为抽象类,不能为具体类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。



对象适配器模式缺点:



与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。



实现



角色



  • Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。

  • Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对 Adaptee 和 Target 进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承 Target 并关联一个 Adaptee 对象使二者产生联系。

  • Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

类适配器



首先有一个已存在的将被适配的类:



public class Adaptee {
public void adapteeRequest() {
System.out.println("被适配者的方法");
}
}



定义一个目标接口:



public interface Target {
void request();
}

如果通过一个适配器类,实现 Target 接口,同时继承了 Adaptee 类,然后在实现的 request() 方法中调用父类的 adapteeRequest() 即可实现



public class Adapter extends Adaptee implements Target{
@Override
public void request() {
//...一些操作...
super.adapteeRequest();
//...一些操作...
}
}
对象适配器



对象适配器与类适配器不同之处在于,类适配器通过继承来完成适配,对象适配器则是通过关联来完成,这里稍微修改一下 Adapter 类即可将转变为对象适配器



public class Adapter implements Target{
// 适配者是对象适配器的一个属性
private Adaptee adaptee = new Adaptee();
@Override
public void request() {
//...
adaptee.adapteeRequest();
//...
}
}

观察者模式(Observer Pattern)

依赖倒置原则(Dependency 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 Driver {
private Car car;
public Driver(Car car) {
this.car = car;
}
public void drive() {
this.car.run();
}
}



Setter方法传递依赖对象

public class Driver {
private Car car;
public void setCar(Car car) {
this.car = car;
}
public void drive() {
this.car.run();
}
}

接口声明依赖

public class Driver {
public void drive(Car car) {
car.run();
}
}



好莱坞原则



don't call us, we'll call you



因为在好莱坞,把简历递交给演艺公司后就只有回家等待。由演艺公司对整个娱乐项的完全控制,演员只能被动式的接受公司的差使,在需要的环节中,完成自己的演出。



为什么有时候依赖倒置原则又被称为好莱坞原则



好莱坞有一个角色,找演员来演



好莱坞相当于高层,演员们相当于低层,他们间的联系是通过某个角色,



这个角色由哪个演员来演都可以,并不依赖于具体某个演员,



整个流程有点像面向接口编程。



📌两个原则强调的点应该是不一样的,查到的资料给我的感觉是:

好莱坞原则:强调的是高层对底层的主动调用

依赖倒置原则:强调的是面向接口编程

有点类似,但又不全是。



里氏替换原则(Liskov Substitution Principle)



单一职责原则(Single Responsibility Principle)



接口隔离原则(Interface Segregation Principle)



发布于: 2020 年 09 月 27 日 阅读数: 18
用户头像

leo

关注

还未添加个人签名 2018.03.23 加入

还未添加个人简介

评论

发布
暂无评论
面向对象设计原则