作业2:读书笔记——软件设计原则设计模式

Posted 茶不予

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了作业2:读书笔记——软件设计原则设计模式相关的知识,希望对你有一定的参考价值。

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/2022softwarecodedevelopmenttechnology
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/2022softwarecodedevelopmenttechnology/homework/12492
这个作业的目标 1.阅读软件设计原则、设计模式的相关书籍。

2.对书的内容进行概括、阐述自己的见解并写成读书笔记。

书籍详情

  • 书籍名称:《软件秘笈:设计模式那点事》

  • 作者:郑阿奇

  • 出版时间:2011年11月1日

  • 出版社:电子工业出版社

  • ISBN:9787121147821

本书由郑阿奇主编,在第1章软件设计模式概述后,从第2章到第24章诠释23个软件设计模式。每一种都从一 个生活故事开始,然后是模式定义、模式分析、模式实现、设计原则和使用场台。模式实现通过Eclipse中的Java工程展开,采用软件编程诠释设计模式故事中的情节和操作,非常有趣。在这个基础上,总结该软件设计模式的设计原则,最后提出使用场合。第25章对各种软件设计模式进行系统总结,第26章是各种软件设计模式的综合应用。
《软件秘笈:设计模式那点事》适合软件开发专业的学生、软件开发人员学习参考,也可作为高等学校有关课程的教材和参考书。
——摘自书籍简介

读书笔记

一、主要内容

本书以生活中常见的例子作为讲解材料,用生动活泼的语言解释了设计模式的原理、设计原则和适用场景。主要介绍了23种设计模式,可以分为类模式和对象模式。

类模式用来处理类和子类之间的关系,这些关系通过继承建立,是静态的,在编译时刻便确定下来了;对象模式是处理对象间的关系,这些关系在运行时是可以变化的,更具动态性。

按照目的来分的话,设计模式可以分为创建型模式、结构型模式和行为型模式。 创建型模式用来处理对象的创建过程,结构型模式用来处理类或者对象的组合,行为型模式用来对类或对象怎样交互和怎样分配职责进行描述。

本书介绍的设计模式总结如下:

创建型模式

  • 1.工厂方法模式

    • 通过继承的方式实现应用程序的解耦,让子类决定实例化哪一个具体类型实例对象。
    • 设计原则采用“开-闭”原则、依赖倒置原则。
    • 适用场景:
      (1)当子类型可能会有很多,以后需要不断增添不同的子类实现时;
      (2)当一个系统尚在框架设计阶段,还不知道将来需要实例化哪些具体类时;
      (3)系统设计之初不需要具体对象的概念(或者说没有具体对象的概念)。
  • 2.抽象工厂模式

    • 提供一个接口,用于创建相关或者依赖对象的家族,而不需要指定具体的实现类。
    • 设计原则采用“开-闭”原则。
    • 适用场景:
      (1)创建产品家族,相关产品集合在一起使用的时候;
      (2)想要提供一个产品类库,并只想显示其接口而不是实现时;
      (3)通过组合的方式使用工厂时。
  • 3.建造者模式

    • 将复杂对象的创建与表示分离,使得同样的构建过程可以创建不同的表示。
    • 设计原则采用分步骤创建复杂对象、构建和表示分离、单一职责原则。
    • 适用场景:
      (1)当生成的产品对象内部具有复杂的结构时;
      (2)当复杂对象需要与表示分离,可能需要创建不同的表示时;
      (3)当需要向客户隐藏产品内部结构的表现时。
  • 4.原型模式

    • 用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
    • 设计原则采用考虑产生对象的复杂度和类复用、结合系统结构考虑使用浅复制还是深复制。
    • 适用场景:
      (1)产生对象过程比较复杂,初始化需要许多资源时;
      (2)希望框架原型和产生对象分开时;
      (3)同一个对象可能会供其他调用者同时调用访问时。
  • 5.单例模式

    • 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
    • 适用场景:当在系统中某个特定的类对象实例只需要有一个的时候,可以使用单例设计模式。需要注意的是,只有真正有“单一实例”的需求时才可使用。

结构型模式

  • 1.对象适配器模式

    • 把一个类的接口转换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作
    • 设计原则采用面向接口和抽象编程、“开-闭”原则。
    • 适用场景:
      (1)软件系统结构需要升级或扩展,又不想影响原有系统的稳定运行的时候;
      (2)转换类之间的差别不是太大的时候;
      (3)想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作的时候。
  • 2.桥接模式

    • 将抽象化与实现化脱耦,使得二者可以独立地变化
    • 设计原则采用使用聚合关联、抽象化层次和实现化层次脱耦。
    • 适用场景:
      (1)不希望在抽象类和它的实现部分之间有一个固定的绑定关系;
      (2)类的抽象及实现都应该可以通过生成子类的方法加以扩充;
      (3)对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。
  • 3.组合模式

    • 将对象组合成树形结构以表示“部分-整体”的层次结构就是组合模式。组合模式使得用户对单个对象和组合对象的使用具有一致性。
    • 设计原则采用统一对待个别对象和组合对象、面向抽象编程。
    • 适用场景:
      (1)想表示对象的“部分-整体”层次结构的时候;
      (2)希望用户忽略组合对象与单个对象的不同,用户将统一使用组合结构中的所有对象的时候。
  • 4.装饰者模式

    • 装饰者模式是在不改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
    • 设计原则采用封装变化部分、开放扩展关闭修改、面向抽象编程、优先使用对象组合。
    • 适用场景:
      (1)当我们需要为某个现有的对象动态地增加一个新的功能或职责时,可以考虑使用装饰模式;
      (2)当某个对象的职责经常发生变化或者经常需要动态地增加职责,避免为了适应这样的变化而增加继承子类扩展的方式,因为这种方式会造成子类膨胀的速度过快,难以控制,此时可以使用装饰者模式。
  • 5.外观模式

    • 外观模式为子系统中的一组接口提供一个统一的高层接口,使子系统更容易使用。
    • 最少知识原则、封装变化部分。
    • 适用场景:
      (1)一个软件系统的复杂度比较高,需要一个更高级别的简单接口简化对子系统的操作时;
      (2)当使用端与实现类之间有太多的相依性,需要降低使用端与子系统或子系统间的耦合性,增加子系统的独立性时;
      (3)当子系统是相互依存的,需要层级化子系统,简化子系统之间的相依性的时候,可以使用外观模式。
  • 6.享元模式

    • 通过复用内存中已存在的对象,降低系统创建对象实例的性能消耗。
    • 共享细粒度对象、隔离变化部分。
    • 适用场景:
      (1)当系统中某个对象类型的实例较多的时候;
      (2)在系统设计中,对象实例进行分类后,发现真正有区别的分类很少的时候。
  • 7.代理模式

    • 两个对象参与处理同一请求,接收的请求由代理对象委托给真实对象处理,代理对象控制请求的访问,它在客户端应用程序与真实目标对象之间起到一个中介桥梁的作用。
    • 延迟加载、单一职责原则。
    • 适用场景:
      (1)远程代理为一个对象在不同的地址空间提供局部代理。
      (2)虚拟代理中,若一个对象的创建非常耗时,可通过代理对象去调用,在真实对象创建前,返回一个假的调用,等真实对象创建好了,这时返回给客户端的就是一个真实对象的相应方法调用。
      (3)保护代理控制对原始对象的访问。
      (4)智能指引取代了简单的指针,它在访问对象时执行一些附加操作。

行为型模式

  • 1.责任链模式

    • 很多对象由每一个对象对其下家的引用而连接起来形成一条链。客户端应用请求在这个链上传递,直到链上的某一个对象决定处理此请求。
    • 单一职责原则、“开-闭”原则。
    • 适用场景:
      (1)有多个对象处理同一个请求,具体由哪一个来处理还不确定,只有在运行时才能确定哪个对象处理的情况。
      (2)消息具有多个接收者,而接收对象又是不明确的情况。只需要向其中的一个对象发出消息,由其内部具体处理。
      (3)同一个消息的多个处理对象可能会动态增加或者减少,需要动态地指定的情况。
  • 2.命令模式

    • 命令模式将一个请求封装为一个对象,从而使用户可用不同的请求对客户进行参数化;将请求排队或记录请求日志,支持可撤销的操作。
    • 最少知识原则、“开-闭”原则。
    • 适用场景:
      (1)抽象出待执行的动作以参数化某对象。类似于过程设计中的回调机制,而命令模式正是回调机制的一个面向对象的替代品。
      (2)在不同的时刻指定、排列和执行请求。
      (3)需要支持可撤销的操作、修改日志功能、事务系统。
  • 3.命令模式

    • 解释器模式就是给定一个语言的文法表示,并且定义一个解释器,用来解释语言中的句子。
    • 封装变化部分、“开-闭”原则。
    • 适用场景:
      (1)一种特定类型的问题发生的频率足够高,并且业务规则频繁变化,不断重复出现类似情况。
      (2)业务规则不是过于复杂烦琐,比较容易抽象出语法规则。
      (3)效率不是软件系统中主要考虑的因素。
  • 4.迭代器模式

    • 提供了一种模式顺序访问一个集合对象中的各个元素功能,而又不暴露其内部的表示。
    • 单一职责原则、“开-闭”原则。
    • 适用场景:
      (1)访问一个集合对象的内容,而无须暴露它的内部表示;
      (2)支持对集合对象的多种遍历方式;
      (3)为遍历不同的集合对象结构提供一个统一的接口。
  • 5.中介者模式

    • 用一个中介对象来封装一系列对象之间的交互,使各个对象中不需要显式地引用其他对象实例,从而降低各个对象之间的耦合度,并且可以独立地改变对象间的交互关系。
    • 对象依赖转化、集中控制。
    • 适用场景:
      (1)一组对象以定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解。注意是多个对象之间相互依赖。
      (2)想定制一个分布在多个类中的行为,而不想生成太多的子类的场合。
      (3)产品架构的研发,更需要易于扩展的场合。
  • 6.备忘录模式

    • 备忘录模式是在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
    • 封装边界的保持、双重接口实现。
    • 适用场景:
      (1)需要在某一时刻恢复一个对象先前的状态时;
      (2)需要在外部保存对象某一时刻的状态,但如果用一个接口来让其他对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。
  • 7.备忘录模式

    • 定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
    • 单一职责原则、“开-闭”原则、依赖倒置原则。
    • 适用场景:
      (1)当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,需要将这两个方面分别封装到独立的对象中,彼此独立地改变和复用的时候;
      (2)当一个系统中一个对象的改变需要同时改变其他对象内容,但是又不知道待改变的对象到底有多少个的时候;
      (3)当一个对象的改变必须通知其他对象做出相应的变化,但是不能确定通知的对象是谁的时候。
  • 8.状态模式

    • 一个对象的内在状态改变时允许改变其行为,这个对象看起来就像是改变了其类。
    • 单一职责原则、“开-闭”原则。
    • 适用场景:
      (1)一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变其行为;
      (2)一个操作中含有庞大的多分支结构,并且这些分支决定于对象的状态。
  • 9.策略模式

    • 定义一系列的算法,将每一种算法封装起来并可以相互替换使用。
    • 单一职责原则、“开-闭”原则。
    • 适用场景:
      (1)当多个类的表现行为不同,需要在运行时动态选择具体要执行的行为的时候;
      (2)需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其他方式实现的时候;
      (3)需要隐藏具体策略(算法)的实现细节,各个具体策略(算法)彼此独立的时候;
      (4)当一个类中出现了多种行为,而且在一个操作中使用多个条件分支来判断使用多种行为的时候,可以使用策略模式将各个条件分支的动作植入具体策略中实现。
  • 10.模板方法模式

    • 定义一个操作中的算法骨架,而将一些实现步骤延迟到子类当中。
    • 好莱坞原则、“开-闭”原则。
    • 适用场景:
      (1)一次性实现一个算法不变的部分,并将可变的行为留给子类来实现;
      (2)各子类中具有公共行为的时候,应被提取出来并集中到一个公共父类中以避免代码重复;
      (3)当需要控制子类扩展的时候。模板方法在特定点调用钩子操作,这样就只允许在这些点进行扩展。
  • 11.访问者模式

    • 访问者模式是表示一个作用于某对象结构中的各元素的操作,它使用户可以在不改变各元素类的前提下定义作用于这些元素的新操作。
    • 单一职责原则、“开-闭”原则。
    • 适用场景:
      (1)如果在一个对象结构中包含很多不同类型对象,它们有不同的接口,而想对这些不同对象实施一些依赖于其具体类的操作。
      (2)需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而想避免让这些操作与这些对象的类关联起来。访问者模式使得可以将相关的操作集中起来,单独定义在一个类中。
      (3)当该对象结构被很多应用共享时,用访问者模式让每个应用仅包含需要用到的操作。
      (4)定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。

二、实际运用

在了解了软件设计模式后,我才发现其实在以前的开发过程中已经有比较粗浅地学习并使用过相关的内容,例如工厂开发模式,就是我们在代码开发(特别是JAVA Script 项目开发)中常用的一种设计模式。

三、心得体会

设计模式可以说是为了提高代码的可读性、可扩展性、可复用性等等特点而产生的一种代码架构理念,可以说只有深刻的理解了这些概念背后的哲学思想才能更好的理解设计模式。在设计模式中有很多思想,比如开闭原则、高内聚低耦合等思想,这些思想使得代码的更新换代的时候能够尽可能少的甚至不用修改之前的代码,直接加入新的内容,可以提高软件的开发周期,便于维护和升级,便于查找和纠错,易于扩展和使用。虽然分成了三类设计模式,但他们是相互关联的,有的设计模式内部其实是使用了别的设计模式作为支撑的,所以在学习过程中我也需要融会贯通,从根本上理解设计原理,而不是死记硬背其模式的定义和使用场景。
通过阅读本书以及撰写读书笔记的过程,我对软件设计原则和设计模式有了更深入的学习,在今后的软件开发中,我也将尝试运用这些方法进行设计,力图让自己的开发过程和代码结构更加清晰明了。

四、截图

架构整洁之道 7~12章读书笔记

第3部分 设计原则

如果建筑的架构设计不佳,那么其所用的砖头质量再好也没有用。这就是SOLID设计原则所要解决的问题。

SOLID原则的主要作用就是告诉我们如何将数据和函数组织成为类,以及如何将这些类链接起来成为程序。

我们为软件构建中层结构的主要目标如下:

  • 使软件可容忍被改动。
  • 使软件更容易被理解。
  • 构建可在多个软件系统中复用的组件。

SOLID原则应该直接紧贴于具体的代码逻辑之上,这些原则是用来帮助我们定义软件架构中的组件和模块的。

SRP:单一职责原则

一个软件系统的最佳结构高度依赖于开发这个系统的组织的内部结构。这样,每个软件模块都有且只有一个需要被改变的理由。

OCP:开闭原则

如果软件系统想要更容易被改变,那么其设计就必须允许新增代码来修改系统行为,而非只能靠修改原来的代码。

LSP:里氏替换原则

如果想用可替换的组件来构建软件系统,那么这些组件就必须遵守同一个约定,以便让这些组件可以相互替换。

ISP:接口隔离原则

这项设计原则主要告诫软件设计师应该在设计中避免不必要的依赖。

DIP:依赖反转原则

高层策略性的代码不应该依赖实现底层细节的代码,恰恰相反,那些实现底层细节的代码应该依赖高层策略性的代码。

第7章 SRP:单一职责原则

任何一个软件模块都应该有且仅有一个被修改的原因。

任何一个软件模块都应该只对某一类行为者负责。

多人为了不同的目的修改了同一份源代码,这很容易造成问题的产生。而避免这种问题产生的方法就是将服务不同行为者的代码进行切分。

第8章 OCP:开闭原则

设计良好的计算机软件应该易于扩展,同时抗拒修改。

如果A组件不想被B组件上发生的修改所影响,那么就应该让B组件依赖于A组件。

软件系统不应该依赖其不直接使用的组件

OCP是我们进行系统架构设计的主导原则,其主要目标是让系统易于扩展,同时限制其每次被修改所影响的范围。实现方式是通过将系统划分为一系列组件,并且将这些组件间的依赖关系按层次结构进行组织,使得高阶组件不会因低阶组件被修改而受到影响。

第9章 LSP:里氏替换原则

果对于每个类型是S的对象o1都存在一个类型为T的对象o2,能使操作T类型的程序P在用o2替换o1时行为保持不变,我们就可以将S称为T的子类型。

LSP可以且应该被应用于软件架构层面,因为一旦违背了可替换性,该系统架构就不得不为此增添大量复杂的应对机制。

第10章 ISP:接口隔离原则

任何层次的软件设计如果依赖了它并不需要的东西,就会带来意料之外的麻烦。

第11章 DIP:依赖反转原则

如果想要设计一个灵活的系统,在源代码层次的依赖关系中就应该多引用抽象类型,而非具体实现。

在应用DIP时,我们也不必考虑稳定的操作系统或者平台设施,因为这些系统接口很少会有变动。我们主要应该关注的是软件系统内部那些会经常变动的(volatile)具体实现模块,这些模块是不停开发的,也就会经常出现变更。

稳定的抽象层

  • 应在代码中多使用抽象接口,尽量避免使用那些多变的具体实现类。
  • 不要在具体实现类上创建衍生类。
  • 不要覆盖(override)包含具体实现的函数。在这里,控制依赖关系的唯一办法,就是创建一个抽象函数,然后再为该函数提供多种具体实现。
  • 应避免在代码中写入与任何具体实现相关的名字,或者是其他容易变动的事物的名字。

工厂模式

利用抽象工厂模式来管理依赖关系

image

源代码依赖方向永远是控制流方向的反转——这就是DIP被称为依赖反转原则的原因。

任意一个软件都能反映出其制作团队的组织结构,这是因为人们会以反映他们组织形式的方式工作。换句话说,分散的团队可能用分散的架构生成系统。项目团队的组织结构中的优点和弱点都将不可避免地反映在他们生成的结果系统中

以上是关于作业2:读书笔记——软件设计原则设计模式的主要内容,如果未能解决你的问题,请参考以下文章

作业二:读书笔记

读书笔记

大话设计模式读书笔记--5个原则

大话设计模式读书笔记——开闭原则

架构整洁之道 7~12章读书笔记

暗时间的一些读书笔记