哈工大软件构造课程博客(四)——软件设计原则及设计模式
设计原则(SOLID)
五大设计原则:
- (SRP) The Single Responsibility Principle 单一责任原则
- (OCP) The Open-Closed Principle 开放-封闭原则
- (LSP) The Liskov Substitution Principle Liskov替换原则
- (DIP) The Dependency Inversion Principle 依赖转置原则
- (ISP) The Interface Segregation Principle 接口聚合原则
单一责任原则:不应该有多于1个原因让你的ADT发生变化,否则就拆分开,即一个类有一个责任
- 为什么要这么做(不良后果):
- 会导致引入额外的包,占据资源
- 导致频繁的重新配置、部署等
开放/封闭原则:对扩展性开发,对修改封闭
- 模块的行为应该是可扩展的,从而该模块可表现出新的行为以满足需求的变化
- 但模块本身的代码是不应该被修改的
- 扩展模块的行为的一般途径是修改模块内部实现
- 如果一个模块不能被修改,那么它通常被认为是具有固定的行为
Liskov替换原则:子类型必须能够替换其基类型
- 子类型可以增加方法,但不可删
- 子类型需要实现抽象类型中的所有未实现方法
- 子类型中重写的方法必须有相同或子类型的返回值或者符合co-variance的参数
- 子类型中重写的方法必须使用同样类型的参数或者符合contra-variance的参数
- 子类型中重写的方法不能抛出额外的异常
- 前置条件不能强化
- 后置条件不能弱化
- 不变量要保持
- 子类型方法参数:逆变
- 子类型方法的返回值:协变
- 异常类型:协变
接口隔离原则
- 不能强迫客户端依赖于它们不需要的接口:只提供必需的接口
转置依赖原则
- 抽象的模块不应依赖于具体的模块
- 具体应依赖于抽象
- 换句话说:delegation的时候,要通过interface建立联系,而非具体子类
设计模式
设计模式包括:adapter、decorator、strategy、template、iterator/iterable、factory method、visitor
- 适配器模式(adapter)
适配器模式是作为两个不兼容的接口之间的桥梁,这种类型的设计模式属于结构型模式,结合了两个独立接口的功能。这种模式设计到一个单一的类,该类负责加入独立或不兼容的接口功能,通过增加一个接口,将已存在的子类封装起来,client面向接口编程,从而隐藏了具体子类 - 装饰器模式(decorator)
装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有类的一个包装,这种模式创建了一个装饰类,用来包装原有的类,并在保持类的方法签名的完整性的前提下,提供了额外的功能,通过对每一个特性构造子类,通过委派机制,以递归的方式增加到对象上 - 策略模式(strategy)
策略模式中,一个类的行为或其算法可以在运行时更改,我们创建表示各种策略的对象和一个行为随着策略对象而改变的对象,为不同的实现算法构造抽象接口,利用delegations,运行时动态传入client倾向的算法类实例,改变对象的执行算法
strategy则强调对ADT内部某些要实现的功能的相应算法的灵活替换。这些算法是ADT功能的重要组成部分,只不过是delegate到外部的strategy类 - 模板模式(template)
在模板模式中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,烦调用将以抽象类中的定义的方法进行。共性的步骤在抽象类内公共实现,差异化步骤在各个子类中实现。模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现 - 迭代器模式(iterator)
这种模式用于顺序访问集合对象的元素,让自己的集合实现Iterable接口,并实现自己的独特Iterator迭代器(hasNext,next,remove),允许客户端利用这个迭代器进行显示或隐式的迭代遍历。以遍历的方式访问集合数据而无需暴露其内部表示,将“遍历”这项功能delegate到外部的iterator对象 - 工厂模式(factory method)
在工厂模式中,在创建对象时不会客户端暴露创建逻辑,而且是通过使用一个共同的接口来指向新创建的对象
当client不知道/不确定要创建哪个具体类的实例,或者不想在client代码中指明要具体创建的实例时,用工厂方法,定义一个用于创建对象的接口,让该接口的子类型决定实例化哪一个类,从而使一个类的实例话化延迟到子类 - 访问者模式(visitor)
在访问者模式中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。它将数据和作用于数据上的某些特定操作分离开来,在特定的ADT上执行某些特定操作,但是该操作不在ADT内部实现,而是delegate到独立的visitor对象,客户端可灵活扩展/改变visitor的操作方法,而不影响ADT。
Visitor强调的是外部定义某种对ADT的操作,该操作与ADT自身关系不大(只是访问ADT),故ADT内部只需要开放accept(visitor)即可,client通过它设定visitor操作并在外部使用