作业二 软件设计原则、设计模式
| 这个作业属于哪个课程 | 班级链接 |
|---|---|
| 这个作业的要求在哪里 | 作业要求链接 |
| 这个作业的目标 | 了解软件设计的原则和设计模式 |
参考书籍
本次了解软件的设计原则和设计模式主要是通过书籍《面向对象软件工程》,石冬凌等编著。这本书主要阐述了软件工程的基本思想、软件开发过程、面向对象的分析与设计技术及项目管理的内容。在各章节中以软件生命周期阶段为主线,介绍了软件开发过程中的每个阶段需要达成的任务目标、涉及的基本原理及采用的技术。
面向对象设计原则
-
开闭原则
定义:一个软件实体应当对扩展开放,对修改关闭。
在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
开闭原则是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统。
-
依赖倒转原则
定义:抽象不应该依赖于细节,细节应当依赖与抽象,换言之,要针对接口编程,而不是针对现实编程。
类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。
-
里氏代换原则
定义:所有引用基类的地方必须能透明地使用其子类的对象。
有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。当使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。子类可以扩展父类的功能,但不能改变父类原有的功能。
-
单一职责原则
定义:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
虽然单一职责原则如此简单,并且被认为是常识,但是即便是经验丰富的程序员写出的程序,也会有违背这一原则的代码存在。为什么会出现这种现象呢?因为有职责扩散。所谓职责扩散,就是因为某种原因,职责P被分化为粒度更细的职责P1和P2。所以记住,在职责扩散到我们无法控制的程度之前,立刻对代码进行重构。
-
接口隔离原则
定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类C来说不是最小接口,则类B和类D必须去实现他们不需要的方法。将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
-
合成复用原则
定义:尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的。
合成复用原则就是在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用功能的目的。简言之,复用时要尽量使用组合/聚合关系(关联关系),少用继承。
-
迪米特原则
定义:一个对象应该对其他对象保持最少的了解。
类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。尽量降低类与类之间的耦合。迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。
设计模式
-
策略模式
-
意图
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立使用它的客户而变化。
-
适用性
当存在以下情况时使用Strategy模式
许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法。
需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式。
算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
-
结构

-
参与者
Strategy(策略,如Compositor)
—— 定义所有支持的算法的公共接口。Context使用这个接口来调用某ConcreteStrategy定义的算法。
ConcreteStrategy(具体策略,如SimpleCompositor, TexCompositor, ArrayCompositor)
—— 以Strategy接口实现某具体算法。
Context(上下文,如Composition)
—— 用一个ConcreteStrategy对象来配置。
—— 维护一个对Strategy对象的引用。
—— 可定义一个接口来让Stategy访问它的数据。
-
效果
优点:
1)提供了对开闭原则的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为
2)提供了管理相关的算法族的办法
3)提供了一种可以替换继承关系的办法,可以避免多重条件选择语句
4)提供了一种算法的复用机制,不同的环境类可以方便地复用策略类
缺点:
1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类
2)将造成系统产生很多具体策略类
3)无法同时在客户端使用多个策略类
-
单例模式
-
意图
保证一个类仅有一个实例,并提供一个访问它的全局访问点
-
适用性
当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时
当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需改代码就能使用一个扩展的实例时
-
结构

-
参与者
Singleton
—— 定义一个Instance操作,允许客户访问它的唯一实例。Instance是一个类操作(即Smalltalk中的一个类方法和C++中的一个静态成员函数)。
—— 可能负责创建它自己的唯一实例。
-
协作
客户只能通过Singleton的Instance操作访问一个Singleton的实例。
-
效果
优点:
1、由于单例模式要求在全局内只有一个实例,因而可以节省比较多的内存空间;
2、全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用;
3、单例可长驻内存,减少系统开销
缺点
1、单例模式的扩展是比较困难的;
2、赋于了单例以太多的职责,某种程度上违反单一职责原则
3、单例模式是并发协作软件模块中需要最先完成的,因而其不利于测试;
4、单例模式在某种情况下会导致“资源瓶颈”
-
适配器模式
-
意图
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
-
适用性
你想使用一个已经存在的类,而它的接口不符合你的需求
你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作
(仅使用于对象Adapter)你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口
-
结构

-
参与者
Target
—— 定义Client使用的与特定领域相关的接口
Client
—— 与符合Target接口的对象协同
Adaptee
—— 定义一个已经存在的接口,这个接口需要适配
Adapter
—— 对Adapter的接口与Target接口进行适配
-
协作
Client在Adapter实例上调用一些操作。接着适配器调用Adaptee的操作实现这个请求。
-
效果
优点:
将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构 增加了类的透明性和复用性,提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用 灵活性和扩展性非常好 类适配器模式:置换一些适配者的方法很方便 对象适配器模式:可以把多个不同的适配者适配到同一个目标,还可以适配一个适配者的子类
缺点:
(1) 一次最多只能适配一个适配者类,不能同时适配多个适配者;
(2) 适配者类不能为最终类;
(3) 目标抽象类只能为接口,不能为类 对象适配器模式:在适配器中置换适配者类的某些方法比较麻烦
-
工厂模式
-
意图
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类
-
适用性
当一个类不知道它所必须创建的对象的类的时候
当一个类希望由它的子类来指定它所创建的对象的时候
当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候
-
结构

-
参与者
Product
—— 定义工厂方法所创建的对象的接口
ConcreteProduct
—— 实现Product接口
Creator
—— 声明工厂方法,该方法返回一个Product类型的对象。Creator也可以定义一个工厂方法的缺省实现,它返回一个缺省的ConcreteProduct对象
—— 可以调用工厂方法以创建一个Product对象
ConcreteCreator
—— 重定义工厂方法以返回一个ConcreteProduct实例
-
协作
Creator依赖玉它的子类来定义工厂方法,它返回一个适当的ConcreteProduct实例
-
效果
工厂方法不再将与特定应用有关的类绑定到你的代码中。代码仅处理Product接口;因此它可以与用户定义的任何ConcreteProduct类一起使用。
工厂方法的一个潜在缺点在于客户可能仅仅为了创建一个特定的ConcreteProduct对象,就不得不创建Creator的子类。当Creator子类不必需时,客户现在必然要处理类演化的其他方面;但是当客户无论如何必须创建Creator的子类时,创建子类也是可行的。
-## 运用体会
在之前进行课程设计的实际操作中,有运用过Spring、Spring MVC等架构进行开发,其中运用的一个模式是工厂模式。使用工厂模式一个最大的好处是极大限度的降低了代码模块之间的耦合程度,降低了后续功能的修改添加的工作难度。
-
截图

浙公网安备 33010602011771号