设计模式之面向对象--面向对象设计的基本原则和基于职责设计对象

最近一直在看设计模式方面的东西,觉得设计模式中很多都是和面向对象融合在一起的,在此抛开传统教科书上的面向对象的几大特性,而谈一些更加本质或者实用的理解: 1、OO(面向对象)技术的最大威力:OO的最大威力并非来自于继承,而是来自于封装的行为。理解对象最佳的方式就是将其看作是“具有职责的东西”,对象应该自己负责自己,并且清楚地定义自己的职责,即封装自己的行为。做到这一点,在对象层面上也就做到了软件设计所一直推崇的高内聚低耦合。 2、对象的三个视角:     1)概念视角:在这个层次上对象是一组职责,此刻应该明确软件要负责什么;     2)规约视角:在这个层次上对象是一组可以被其他对象或自身调用的方法,此刻开发者应该明确怎样使用软件;     3)实现视角:在这个层次上对象是代码和数据及它们之间的计算交互,此刻应该明确软件怎样履行自己的职责。 3、职责对于对象的作用:在现实社会中,每个人各司其职,真个社会才会有条不紊的运转着。同样,每一个对象对应该有其自己的职责。对象是由职责决定的。对象能够自己负责自己,从而大大简化了控制程序的任务。 4、适应变化:对于阻止变化,我们无计可施,但是我们对需求变化本省并非无能为力。需求总是在变化着,我们可能无法治知道什么将发生变化,但是我们可以知道哪里将发生变化,并提前做好防范措施。这也是OO技术中十分重要的一部分:封装变化。如果使用封装,并遵循对象自己负责自己的原则,那么能够应向对象的唯一方法就是调用对象的方法。对象自己负责的越多,接口设计的越稳定,控制程序需要负责的就越少,同样当变化发生时所影响的面积就越小,也就有效的限制了变化,可以方便的适应变化。 4、老生常谈“耦合与内聚”:软件设计的根本——不仅限于OO。     1)内聚性(cohesion):同一个模块/例程中操作之间联系的紧密度。我们追求的是高内聚。高内聚使对象在概念上有更好的一致性。同时,高内聚也以为这对象有比较单一的职责。     2)耦合性(coupling):两个模块/例程之间联系的紧密程度。高耦合意味着系统各模块/对象之间有着极其复杂的千丝万缕的联系,牵一发而动全身,对需求变化时,如果你维护这样的系统,或者考虑对其进行重构,或者建议你辞职好了……

面向对象设计的一些基本原则

1. 优先使用(对象)组合,而非(类)继承。Favor Composition Over Inheritance。    组合:是一种通过创建一个组合了其它对象的对象,从而获得新功能的复用方法。组合将功能委托给所组合的一个对象,从而获得新功能。有些时候也称之为“聚合”(aggregation)或“包容”(containment)。继承:是一种通过扩展一个已有对象的实现,从而获得新功能的复用方法。

2. 针对接口编程,而非(接口的)实现。Program To An Interface, Not An Implementation。    客户无需知道所使用对象的特定类型,只需要知道对象拥有客户所期望的接口    接口是一个对象在对其它的对象进行调用时所知道的方法集合。一个对象可以有多个接口,类型是对象的一个特定的接口。不同的对象可以具有相同的类型,而且一个对象可以具有多个不同的类型。一个对象仅能通过其接口才会被其它对象所了解。实现继承(类继承):一个对象的实现是根据另一个对象的实现来定义的。接口继承(子类型化):描述了一个对象可在什么时候被用来替代另一个对象。优点:Client不必知道其使用对象的具体所属类。一个对象可以很容易地被(实现了相同接口的)的另一个对象所替换。对象间的连接不必硬绑定(hardwire)到一个具体类的对象上,因此增加了灵活性。松散藕合(loosens coupling)。增加了重用的可能性。提高了(对象)组合的机率,因为被包含对象可以是任何实现了一个指定接口的类。类继承通常为“白箱复用”,对象组合通常为“黑箱复用”。继承在某种程度上破坏了封装性,子类与父类耦合度高;而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。

3. 开放-封闭法则(OCP):模块应对扩展开放,而对修改关闭。模块应尽量在不修改原代码的情况下进行扩展。Software Entities Should Be Open For Extension,But Closed For Modification。此原则是由"Bertrand Meyer"提出的。开放-封闭法则认为我们应该试图去设计出永远也不需要改变的模块。我们可以添加新代码来扩展系统的行为。我们不能对已有的代码进行修改。在进行面向对象设计时要尽量考虑接口封装机制、抽象机制和多态技术。符合OCP的模块需满足两个标准:可扩展,即“对扩展是开放的”(Open For Extension)-模块的行为可以被扩展,以需要满足新的需求。不可更改,即“对更改是封闭的”(Closed for Modification)-模块的源代码是不允许进行改动的。

4. Liskov替换法则(LSP):子类应当可以替换父类并可以出现在父类能够出现的任何地方。Function That Use Referennces To Base(Super) Classes Must Be Able To Use Objects Of Derived(Sub) Classes Without Knowing It 。为了保持LSP(并与开放-封闭法则一起),所有子类必须符合使用其基类的client所期望的行为。它同样可以从Bertrand Meyer 的DBC (Design by Contract) 的概念推出。

5. 依赖原则 (the Dependency Inversion Principle DIP):在进行业务设计时,与特定业务有关的依赖关系应该尽量依赖接口和抽象类,而不是依赖于具体类。具体类只负责相关业务的实现,修改具体类不影响与特定业务有关的依赖关系。

6. 接口分离原则(the Interface Segregation Principle ISP):采用多个与特定客户类有关的接口比采用一个通用的涵盖多个业务方法的接口要好。

7. 单一职责原则(SRP):一个类应该仅有一个引起它变化的原因。封装变化:使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合。

基于职责设计对象(GRASP):General Responsibility Assignment Software Pattern 类的职责是类的契约和义务,包括行为职责(初始化其他对象、控制和协调其他对象的活动)和认知职责(对私有封装数据的认知、对其他对象的认知、对其能够导出或计算出的对象的认知)。GRASP是关于对象设计和职责分配的一组基本原则,有以下原则:

1. 创建者:如果以下条件之一(越多越好)为真时,将创建A的职责分配给B B包含或组成聚集A B记录A B直接使用A B具有A初始化所需要的数据,并且在创建A的时候会将这些数据传递给A 基本意图是寻找在任何情况下都与被创建者有直接连接的创建者。

2. 信息专家:把职责分配给具有实现这个职责所必需信息的信息专家(分配职责从清晰买描述对象职责开始,把职责分配给具有完成此职责所需信息的对象。“知其责,行其事”)。

3. 低耦合:分配职责,使耦合性尽可能低(低耦合是制定设计决策的时候必须牢记的原则)。

4. 控制器:把职责分配给能够代表以下选择之一的对象(控制器是在UI层首先接收和协调系统操作信息的第一个对象)。代表整个系统、跟系统、运行软件的设备或主要的子系统。这些事外观控制器的所有变体。代表用例场景,在该场景中发生的事件通常命名为<..>Handler/Session等。这些事用例或会话控制器。

 5. 高内聚:分配职责要保持较高的内聚性(高内聚:元素具有高度的相关的职责,而且没有过多的职责)。

 6. 多态:当相关选择或行为随类型(类)有所不同时,使用多态操作为变化的行为分配职责。

7. 纯虚构:对认为制造的一组类分配一组高内聚的职责,该类并不代表问题域的概念—虚构的事物,用以支持高内聚、低耦合和复用。

8. 间接性:将职责分配给中介对象,使其作为其他构件或服务之间的媒体,以避免他们之间的直接耦合。

9. 防止变异:识别或预计变化或不稳定之处,分配职责用以在这些变化之处创建稳定的接口

 

 

1。软件的可维护性与可复用性 软件的维护就是软件的再生。系统的设计目标: 可扩展性,灵活性,可插入性。 可扩展性: 新的功能很容易集成到现有的系统中去,而不影响到系统的其他模块。 灵活性: 允许代码修改平稳的发生。当修改一处时不至于影响到另一处,这样可以缩小维护的代价。 可插入性: 容易用一个类替换已经存在的类。只要接口一致,更改实现类不影响类的使用者。 软件的复用可以提高软件的生产率,并且恰当的复用可以提高软件的可维护性。 在以前,复用主要是代码,函数,结构的复用,而现在复用主要针对类,接口,组件等等。但是复用并不一定会保证软件的可维护性。不能因为代码的重复等原因就复用,需要根据具体的情况来分析。要想通过复用来加强系统的可维护性,必须保证复用是支持可维护性的复用。 下面的一些设计原则可以用来指导实践。 2。开-闭 原则(OCP) 开闭原则是面向对象可复用的基石。它主要指:一个软件实体对扩展开放,对修改关闭。在设计一个模块的时候,应当是这个模块在不被修改的前提下被扩展。满足这个原则的系统在一个较高层次上实现了复用,也是易于维护的。 那如何才能满足开闭原则呢?抽象化是关键。要区分开抽象层和实现层。在一个软件系统中,抽象层应该是相对稳定的,而实现层是可以改变和扩展的。 开闭原则也是对可变性的封装原则。找到系统的可变因素,并将其封装起来。把一种可变性封装为一个对象,那么这种可变性的不同表象就是这个类的具体子类。 3。里氏代换原则(LSP) 里氏代换原则是继承复用的基石:在任何父类出现的地方都可以用它的子类来替代。例如正方形和长方形。正方形是一种特殊的长方形。但是正方形却不能作为长方形的子类。因对长方形的操作并不一定能套用在正方形上。如:resize().如果正方形作为长方形的子类的话,那么就会出现很多问题。不符合里氏代换原则,正方形就不能作为长方形的子类,应该把正方形和长方形都作为四边形的子类。在实际,设计类的阶层体系结构时,这是一条很重要的原则。不应该作为子类不能硬套。 4。依赖倒转原则(DIP) 依赖倒转原则是:要依赖于抽象,不要依赖于具体的实现。 为什么叫依赖倒转原则呢? 在传统的过程性系统中,高层的模块依赖于低层次的模块,抽象层次依赖于具体层次。这样导致了底层的任何改变都会影响到上层。这样的软件系统没有可维护性而言。 抽象层次应该不依赖于具体的实现细节,这样才能保证系统的可复用性和可维护性,这也就是所谓的倒转。 在实际中如何应用这一原则呢?要针对借口编程,而不针对实现编程。那么当实现变化时,不会影响到其他的地方。 在java中应当使用接口和抽象类来进行变量的类型声明,参数的类型声明,方法的返回值类型,等等。比如: List list = new Linkedlist(); 以抽象的方式进行耦合是依赖倒转原则的关键。 依照依赖倒转原则,在系统中会出现大量的类,如抽象类和接口,因为它假定所有的具类都是有可能变化的。但实际上这也不总是正确的。有一些具体类是非常稳定的,就不需要为他发明一种新的类型。 5。 接口隔离原则(ISP) 使用多个专门的借口总是比使用单一的总接口要好。一个接口应该仅代表一个角色,而不应该把所有的操作都封装到一个接口当中。准确地划分角色和其所对应的接口,不应该把没有关系的接口合并到一起。 6。合成/聚合复用原则 聚合: 表示拥有或整体与部分的关系。合成:更强的聚合关系。整体负责部分的生命周期,整体和部分是不可分的,部分是不能被共享的。比如孙悟空 ,他的四肢, 和他的武器。悟空和四肢的关系就是合成,而和武器之间的关系就是聚合。 在面向对象中,有继承和合成/聚合两种基本服用方法。合成/聚合是将已有对象作为自己的成员,新对象调用对象已有的功能。这有很多优点: 1) 这种复用是黑箱操作,把成员对象的细节封装起来。 2)这种复用可以动态的改变,新对象可以动态的引用其他的同类对象。 继承复用有如下的优点和缺点。 1)容易实现。 2)继承复用破坏了包装,将超类的实现细节暴露给子类。 3)同时如果超类发生改变,子类也不得不跟着改变。 4)继承是静态的。 尽量使用和成/聚合而不是继承来实现复用。而区分这两种关系,区分”Has-A”和“Is-A”的关系。能正确的区分两者,应该就能正确的使用这两种复用方式。例如:人和角色。通常使用继承,可以把每个角色作为人的子类。如学生,雇员,经理等。但是继承是静态的,一旦一个人具有了一个角色后就不能再拥有其他的角色。比如一个人是经理,但它也是雇员,也可能是学生。这显然是不合理的。应该采用合成/聚合复用原则。实际上人与角色是“Has-A”而不是”Is-a”的关系,并且只有当满足里氏代换原则的时候才能使“Is-a”的关系。 在java api中 Properties 继承 Hashtable。Proterties具有了Hashtable的所有行为。实际上,Properties根本就不是一种Hashtable.更严重的是把Properties可以向上转型为Hashtable,,绕过Properties接口,调用Hashtable的方法来对Properties操作,会导致Properties的内部矛盾和崩溃。他们之间可以是“Has-a”,但不能是”Is-a”的关系。在实际中,决不能因为代码和功能上的复用而滥用继承,使用继承时一定要满足里氏代换原则。 7。迪米特法则(LoD) 迪米特法则: 一个对象应当对其他对象尽可能少的了解。以下是一些表述: 1) 只和你直接的朋友通信 2) 不要和陌生人讲话。 3)每一个软件单位都对其他单位只有最少的知识。 其目的就是降低各个单元的耦合,提高系统的可维护性。在模块之间,其通信应该只通过彼此的API来通信,而不理会模块的内部工作原理。它可以使各个模块耦合程度降到最低,促进软件的复用。

 

面向对象设计原则--《敏捷软件开发》读书笔记

SRP 单一职责原则
就一个类而言,应该仅有一个引起它变化的原因。
职责即为"变化的原因".
 
(2)OCP 开放封闭原则
软件实体(类、模块、函数等)应该是可以扩展的,但是不可修改。
对于扩展是开放的,对于更改是封闭的.
关键是抽象.将一个功能的通用部分和实现细节部分清晰的分离开来.
开发人员应该仅仅对程序中呈现出频繁变化的那些部分作出抽象.拒绝不成熟的抽象和抽象本身一样重要.
 
(3)LSP Liskov替换原则
子类型必须能替换掉他们的基本类型
 
(4)DIP 依赖倒置原则
抽象不应该依赖于细节。细节应该依赖于抽象。
Hollywood原则: "Don't call us, we'll call you".程序中所有的依赖关系都应该终止于抽象类和接口。针对接口而非实现编程。
任何变量都不应该持有一个指向具体类的指针或引用。
任何类都不应该从具体类派生。
任何方法都不应该覆写他的任何基类中的已经实现了的方法。
 
(5)ISP 接口隔离原则
不应该强迫客户依赖于他们不用的方法。接口属于客户,不属于他所在的类层次结构。
多个面向特定用户的接口胜于一个通用接口。
 
(6)REP 重用发布等价原则
重用的粒度就是发布的粒度.
 
(7)CCP 共同重用原则
一个包中的所有类应该是共同重用的。如果重用了包中的一个类,那么就要重用包中的所有类。
相互之间没有紧密联系的类不应该在同一个包中。
 
(8)CRP 共同封闭原则
包中的所有类对于同一类性质的变化应该是共同封闭的。一个变化若对一个包影响,则将对包中的所有类产生影响,而对其他的包不造成任何影响.
 
(9)ADP 无依赖原则
在包的依赖关系中不允许存在环.
细节不应该被依赖.
 
(10)SDP 稳定依赖原则
朝着稳定的方向进行依赖.
应该把封装系统高层设计的软件(比如抽象类)放进稳定的包中,不稳定的包中应该只包含那些很可能会改变的软件(比如具体类)。
 
(11)SAP 稳定抽象原则
包的抽象程度应该和其他稳定程度一致.
一个稳定的包应该也是抽象的,一个不稳定的包应该是抽象的.
 
(12)DAP(Default Abstraction Principle)缺省抽象原则
在接口和实现接口的类之间引入一个抽象类,这个类实现了接口的大部分操作.
 
(13)IDP(Interface Design Principle)接口设计原则
规划一个接口而不是实现一个接口。
 
(14)BBP(Black Box Principle)黑盒原则
多用类的聚合,少用类的继承。
 
(15)DCSP(Don't Concrete Supperclass Principle)不要构造具体的超类原则
避免维护具体的超类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值