参考文章
开发原则
S单一职责SRP
定义一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。(Every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class.),即又定义有且仅有一个原因使类变更。 原则分析
优点
例子SpringMVC 中Entity,DAO,Service,Controller, Util等的分离。 O开放封闭原则OCP
定义
原则分析
例子设计模式中模板方法模式和观察者模式都是开闭原则的极好体现。 L里氏替换原则LSP
定义第一种定义方式相对严格:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有变化,那么类型S是类型T的子类型。 第二种更容易理解的定义方式:所有引用基类(父类)的地方必须能透明地使用其子类的对象。即子类能够必须能够替换基类能够从出现的地方。子类也能在基类 的基础上新增行为。 (里氏代换原则由2008年图灵奖得主、美国第一位计算机科学女博士、麻省理工学院教授BarbaraLiskov和卡内基.梅隆大学Jeannette Wing教授于1994年提出。其原文如下:Let q(x) be a property provableabout objects x of type T. Then q(y) should be true for objects y of type Swhere S is a subtype of T. ) 原则分析
I接口隔离法则
定义客户端不应该依赖那些它不需要的接口。 另一种定义方法:一旦一个接口太大,则需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。 原则分析1)接口隔离原则是指使用多个专门的接口,而不使用单一的总接口。每一个接口应该承担一种相对独立的角色,不多不少,不干不该干的事,该干的事都要干。
2)使用接口隔离原则拆分接口时,首先必须满足单一职责原则,将一组相关的操作定义在一个接口中,且在满足高内聚的前提下,接口中的方法越少越好。 3)可以在进行系统设计时采用定制服务的方式,即为不同的客户端提供宽窄不同的接口,只提供用户需要的行为,而隐藏用户不需要的行为。 D依赖倒置原则DIP
定义高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述: 1)抽象不应当依赖于细节;细节应当依赖于抽象; 2)要针对接口编程,不针对实现编程。 原则分析1)如果说开闭原则是面向对象设计的目标,依赖倒转原则是到达面向设计'开闭'原则的手段。如果要达到最好的'开闭'原则,就要尽量的遵守依赖倒转原则。可以说依赖倒转原则是对“抽象化”的最好规范!我个人感觉,依赖倒转原则也是里氏代换原则的补充。你理解了里氏代换原则,再来理解依赖倒转原则应该是很容易的。 2)依赖倒转原则的常用实现方式之一是在代码中使用抽象类,而将具体类放在配置文件中。 3)类之间的耦合:零耦合关系,具体耦合关系,抽象耦合关系。依赖倒转原则要求客户端依赖于抽象耦合,以抽象方式耦合是依赖倒转原则的关键。 例子理解这个依赖倒置,首先我们需要明白依赖在面向对象设计的概念: 依赖关系(Dependency):是一种使用关系,特定事物的改变有可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时使用依赖关系。(假设A类的变化引起了B类的变化,则说名B类依赖于A类。)大多数情况下,依赖关系体现在某个类的方法使用另一个类的对象作为参数。在UML中,依赖关系用带箭头的虚线表示,由依赖的一方指向被依赖的一方。 例子:某系统提供一个数据转换模块,可以将来自不同数据源的数据转换成多种格式,如可以转换来自数据库的数据(DatabaseSource)、也可以转换来自文本文件的数据(TextSource),转换后的格式可以是XML文件(XMLTransformer)、也可以是XLS文件(XLSTransformer)等。 由于需求的变化,该系统可能需要增加新的数据源或者新的文件格式,每增加一个新的类型的数据源或者新的类型的文件格式,客户类MainClass都需要修改源代码,以便使用新的类,但违背了开闭原则。现使用依赖倒转原则对其进行重构。
/** 合成/聚合复用原则
定义经常又叫做合成复用原则(Composite ReusePrinciple或CRP),尽量使用对象组合,而不是继承来达到复用的目的。 就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新对象通过向这些对象的委派达到复用已有功能的目的。简而言之,要尽量使用合成/聚合,尽量不要使用继承。 原则分析1)在面向对象设计中,可以通过两种基本方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承。 继承复用:实现简单,易于扩展。破坏系统的封装性;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;只能在有限的环境中使用。(“白箱”复用) 组合/聚合复用:耦合度相对较低,选择性地调用成员对象的操作;可以在运行时动态进行。(“黑箱”复用) 2)组合/聚合可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。 3)此原则和里氏代换原则氏相辅相成的,两者都是具体实现'开-闭'原则的规范。违反这一原则,就无法实现'开-闭'原则,首先我们要明白合成和聚合的概念:
迪米特法则
定义又叫最少知识原则(Least Knowledge Principle或简写为LKP)几种形式定义:
简单地说,也就是,一个对象应当对其它对象有尽可能少的了解。一个类应该对自己需要耦合或调用的类知道得最少,你(被耦合或调用的类)的内部是如何复杂都和我没关系,那是你的事情,我就知道你提供的public方法,我就调用这么多,其他的一概不关心。 法则分析
在迪米特法则中,对于一个对象,其朋友包括以下几类:(1) 当前对象本身(this);(2) 以参数形式传入到当前对象方法中的对象;(3) 当前对象的成员对象;(4) 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友;(5) 当前对象所创建的对象。 任何一个对象,如果满足上面的条件之一,就是当前对象的“朋友”,否则就是“陌生人”。
在狭义的迪米特法则中,如果两个类之间不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。 狭义的迪米特法则:可以降低类之间的耦合,但是会在系统中增加大量的小方法并散落在系统的各个角落,它可以使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联,但是也会造成系统的不同模块之间的通信效率降低,使得系统的不同模块之间不容易协调。 广义的迪米特法则:指对对象之间的信息流量、流向以及信息的影响的控制,主要是对信息隐藏的控制。信息的隐藏可以使各个子系统之间脱耦,从而允许它们独立地被开发、优化、使用和修改,同时可以促进软件的复用,由于每一个模块都不依赖于其他模块而存在,因此每一个模块都可以独立地在其他的地方使用。一个系统的规模越大,信息的隐藏就越重要,而信息隐藏的重要性也就越明显。
例子外观模式Facade(结构型) 迪米特法则与设计模式Facade模式、Mediator模式
Q&A面向对象设计其他原则封装变化 少用继承 多用组合 针对接口编程 不针对实现编程 为交互对象之间的松耦合设计而努力 类应该对扩展开发 对修改封闭(开闭OCP原则) 依赖抽象,不要依赖于具体类(依赖倒置DIP原则) 密友原则:只和朋友交谈(最少知识原则,迪米特法则) 说明:一个对象应当对其他对象有尽可能少的了解,将方法调用保持在界限内,只调用属于以下范围的方法: 该对象本身(本地方法)对象的组件 被当作方法参数传进来的对象 此方法创建或实例化的任何对象 别找我(调用我) 我会找你(调用你)(好莱坞原则) 一个类只有一个引起它变化的原因(单一职责SRP原则) 你能解释一下里氏替换原则吗?严格定义:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象用o1替换o2时,程序P的行为没有变化,那么类型S是类型T的子类型。 通俗表述:所有引用基类(父类)的地方必须能透明地使用其子类的对象。也就是说子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下4层含义: 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。 子类中可以增加自己特有的方法。 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。 什么情况下会违反迪米特法则?为什么会有这个问题?迪米特法则建议“只和朋友说话,不要陌生人说话”,以此来减少类之间的耦合。 给我一个符合开闭原则的设计模式的例子?开闭原则要求你的代码对扩展开放,对修改关闭。这个意思就是说,如果你想增加一个新的功能,你可以很容易的在不改变已测试过的代码的前提下增加新的代码。有好几个设计模式是基于开闭原则的,如策略模式,如果你需要一个新的策略,只需要实现接口,增加配置,不需要改变核心逻辑。一个正在工作的例子是 Collections.sort() 方法,这就是基于策略模式,遵循开闭原则的,你不需为新的对象修改 sort() 方法,你需要做的仅仅是实现你自己的 Comparator 接口。 什么时候使用享元模式(蝇量模式)?享元模式通过共享对象来避免创建太多的对象。为了使用享元模式,你需要确保你的对象是不可变的,这样你才能安全的共享。JDK 中 String 池、Integer 池以及 Long 池都是很好的使用了享元模式的例子。
|
|