首页 > 编程知识 正文

面向对象设计的基本原理,面向对象五个基本原则

时间:2023-05-04 07:28:38 阅读:138711 作者:3884

面向对象设计的五个基本原则SOLID单责任原则开闭原则chdqz替换原则接口隔离原则依赖倒置原则的总结

在编程领域,SOLID是zgdtn在21世纪初引入的存储技术的首字母缩写,指的是面向对象编程和面向对象设计的五个基本原则。 这些原则一起应用后,程序员就有可能开发出容易维护和扩展的系统。 SOLID是以下五个单词的缩写。

单一职责原则) ) ) ) )。

开放关闭打印) )。

liskovsubstitutionprinciple (CHD qz置换原则) ) ) ) ) )。

接口隔离原则

依赖倒置原则)

让我们仔细解读这五个基本原则。

单一职责原则 什么是职责?

在thesingleresponsibilityprinciple (SRP )中角色的定义是“变动的原因”(A reason for change )。 如果有多个修改一个类的动机,则该类有多个作用。 这可能很难理解。 我们通常把责任群体放在一起考虑,所以我们来看看具体的例子。 接下来是称为调制解调器或猫的连接器

界面模式{ public void dial (string pno ); 公共void hang up (; 公共void send (charc; 公共char recv (; }上面的猫界面有两个作用。 第一个是管理连接(dial和hangup ); 二是数据传输(send和recv )

这两个职责必须分开。 因为:

没有共同点,通常因为不同的理由被修正

通常,调用它们的代码属于APP应用程序的不同部分,该部分的代码也因不同的原因而更改。

了解调制解调器优化的设计:

通过分割猫的界面,我们可以在APP的其他部分分开处理猫的设计。 我们还在猫实现中重新结合了这两个职责,除了初始化猫的代码外,在使用面向接口的编程原则后,其他代码不需要依赖于猫的实现。

SRP是最简单的面向对象设计原则,但做正确的事是最难的。 因为我们习惯于合并责任,而不是把它们分开。 而且,分割这些角色是软件设计中真正需要的。

总结以下单一职责原则

核心思想:应该并且只有一个原因引起班级的变更

好处:降低类的复杂性,提高可读性,提高可维护性,提高可扩展性,通过更改降低风险。

需注意:单一职责原则提出了编制程序的标准,衡量的是“职责”或“变化原因”中的接口或类是否设计得很好,但无法衡量“职责”和“变化原因”

开闭原则的开闭原则的英文是Open Closed Principle,简称OCP。

开关的原则是软件实体(模块、类、函数等)必须为对扩展是开放的对修改是关闭的

对扩展是开放的,这意味着软件实体的操作是可扩展的,并且模块可以被扩展以在需求改变时满足需求改变的请求。

关闭修改意味着在扩展软件实体时,不需要修改不需要改动当前的软件实体代码; 不需要重新编辑已完成的类文件; 对于已经编译并打包的模块,不需要重新编译。

两者结合起来,增加新功能的是在现有代码的基础上3358www.Sina.com/(添加模块、类、方法等)、扩展代码)模块、库库

现在我们来看看销售计算机的例子。 首先定义顶级接口Computer,然后定义两个实现类:华硕计算机和苹果Mac。 类层次结构如下图所示。

以上是我们的第一个需求,但随着软件发行的执行,我们的需求必须与市场挂钩,而不是一成不变的。 如果现在是双十一的话,华硕笔记本电脑需要做促销活动。 我们的代码必须增加新的功能。 一些刚入职的新人可能会对原来的代码进行更改。 这一定不符合开关原则吧。 虽然这种做法是最直接、最简单的,但在大多数项目中,单个功能的实现比想象中更复杂,对原始代码进行更改比扩展一种方法实现的风险要高得多。 正确的做法可以这样做:

实施了关于折扣的子类。 它包括有关折扣的方法,这相当于扩展方法。 您可以看到这个子类是AsusComputer的。 那么,为什么不把他设计成共同的折扣班呢? 例如,DiscountComp

uter,所有实现类都继承这个折扣类。这是因为每种实现类的折扣方案可能是不一样的。所以我们最好能把它作为每个实现类的子类单独实现。如果你能确保你的业务中的新功能能兼容所有相关联的需求你也可以共用一个。

小结一下开闭原则就是

核心思想: 尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化

通俗来讲: 一个软件产品在生命周期内,都会发生变化,既然变化是一个既定的事实,我们就应该在设计的时候尽量适应这些变化,以提高项目的稳定性和灵活性。

chdqz替换原则

chdqz替换原则由crdwdm Liskov提出,这个原则很明显,Java的多态或者C++的虚函数本身就允许把指向基类的指针或引用,在调用其方法或函数的时候,调用实际类型的方法或函数。我们来看一个简单的例子:Circle 和 Square 继承了基类 Shape,然后在应用的方法中,根据输入 Shape 对象类型进行判断,根据对象类型选择不同的绘图函数将图形画出来。

void drawShape(Shape shape) { if (shape.type == Shape.Circle ) { drawCircle((Circle) shape); } else if (shape.type == Shape.Square) { drawSquare((Square) shape); } else { …… } }

这种写法的代码既常见又糟糕,它同时违反了开闭原则和chdqz替换原则。

首先看到这样的 if/else 代码,就可以判断违反了(我们刚刚在上个部分讲过的)开闭原则:当增加新的 Shape
类型的时候,必须修改这个方法,增加 else if 代码。其次也因为同样的原因违反了chdqz替换原则:当增加新的Shape 类型的时候,如果没有修改这个方法,没有增加 else if
代码,那么这个新类型就无法替换基类 Shape。

要解决这个问题其实也很简单,只需要在基类 Shape 中定义 draw 方法,所有 Shape 的子类,Circle、Square 都实现这个方法就可以了:

public abstract Shape{ public abstract void draw();}

上面那段 drawShape() 代码也就可以变得更简单:

void drawShape(Shape shape) { shape.draw();}

小结一下chdqz替换原则就是

核心思想: 在使用基类的的地方可以任意使用其子类,能保证子类完美替换基类。

通俗来讲: 只要父类能出现的地方子类就能出现。反之,父类则未必能胜任。

好处: 增强程序的健壮性,即使增加了子类,原有的子类还可以继续运行。

需注意: 如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系 采用依赖、聚合、组合等关系代替继承。

接口隔离原则

接口隔离原则的英文是 SInterface Segregation Principle,缩写为 ISP。这个原则是说:客户端不应该强迫依赖它不需要的接口。

我们在设计微服务或者类库接口的时候,如果部分接口只被部分调用者使用,那我们就需要将这部分接口隔离出来,单独给对应的调用者使用,而不是强迫其他调用者也依赖这部分不会被用到的接口。举一个简单的例子:

public interface UserService { boolean register(String cellphone, String password); boolean login(String cellphone, String password); UserInfo getUserInfoById(long id); UserInfo getUserInfoByCellphone(String cellphone);} public interface RestrictedUserService { boolean deleteUserByCellphone(String cellphone); boolean deleteUserById(long id);} public class UserServiceImpl implements UserService, RestrictedUserService { // ...省略实现代码... }

删除用户是一个非常慎重的操作,我们只希望通过后台管理系统来执行,所以这个接口只限于给后台管理系统使用。如果我们把它放到 UserService 中,那所有使用到 UserService 的系统,都可以调用这个接口。不加限制地被其他业务系统调用,就有可能导致误删用户。

参照接口隔离原则,调用者不应该强迫依赖它不需要的接口,将删除接口单独放到另外一个接口RestrictedUserService 中,然后将 RestrictedUserService 只打包提供给后台管理系统来使用。

小结一下接口隔离原则就是

核心思想: 类间的依赖关系应该建立在最小的接口上

通俗来讲: 建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

需注意: 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情为依赖接口的类定制服务。只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。

依赖倒置原则

依赖倒置原则的英文是 Dependency Inversion Principle,缩写为 DIP。依赖倒置原则说的是:高层模块不依赖低层模块,它们共同依赖同一个抽象,这个抽象接口通常是由高层模块定义,低层模块实现。同时抽象不要依赖具体实现细节,具体实现细节依赖抽象。高层模块就是调用端,低层模块就是具体实现类,抽象就是指接口或抽象类,细节就是实现类。

来看一个简单的例子:假设我们要设计一个很简单的程序,将键盘的输入输出到打印机上。一个简单的设计的程序结构图如下所示。

上面的设计中有三个模块,Copy模块调用Read Keyboard模块来读取输出,然后Copy调用Write Printer模块输出字符。Read Keyboard和Write Printer是两个下层模块,并且很容易被复用。

然而我们的Copy模块却不能被复用于任何不包含键盘和打印机的场景中,而Copy恰恰是这个程序的业务逻辑所在的模块,也是我们最希望能够复用的。

比如,我们还希望将键盘的输入,复制到磁盘文件。我们当然希望复用Copy模块,而事实上,Copy依赖于键盘和打印机,缺一不可,所以不能被复用。我们也可以往Copy中增加一个if条件来支持新的磁盘文件输出,但是这就违背了开闭原则,最终随着功能的变多,代码将变得不可维护。

这个例子中的问题其实是高层级的模块(Copy模块)依赖于层级的模块(Read Keyboard和Write Printer);如果能够找到一个让Copy独立于它所控制的底层级模块的方法,那么我们可以自由地复用这个Copy模块。下图就是一种依赖反转的解决方案。


在这个新的设计中,我们的Copy模块有一个抽象的Reader和一个抽象的Writer。Copy不再直接依赖于具体的实现,不管有几个Reader或Writer的实现,我们都不需要修改Copy。

小结一下依赖倒置原则就是

核心思想: 高层模块不应该依赖底层模块,二者都该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象;

通俗来讲: 依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,互不影响,实现模块间的松耦合。

好处: 依赖倒置的好处在小型项目中很难体现出来。但在大中型项目中可以减少需求变化引起的工作量。使并行开发更友好。

总结

今天的内容一句话概括就是:单一职责原则告诉我们实现类要职责单一;chdqz替换原则告诉我们不要破坏继承体系;接口隔离原则告诉我们在设计接口的时候要精简单一;依赖倒置原则告诉我们要面向接口编程。而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。

在实际开发过程中,并不是一定要求所有代码都遵循设计原则,我们要考虑人力、时间、成本、质量,不是刻意追求完美,要在适当的场景遵循设计原则,体现的是一种平衡取舍,帮助我们设计出更加优雅的代码结构。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。