《结构型模式》——第四、五周作业

 1.结构型模式

1.结构型模式:

     *作用:描述如何将类后对象按某种布局组成更大的结构。

     *核心:类结构型模式(采用继承)和对象结构型模式(采用组合),对象结构型模式比类结构模式具有更大的灵活性。

     *分类:

               1> Adapter适配器模式

          2> Bridge桥接模式

          3> Composite组合模式

          4>Decorator装饰者模式

          5>Facade外观模式

          6>Flyweight享元模式

          7>Proxy代理模式

2.Adapter适配器模式:

      *定义:“适配”即转换,在不改变原有实现的基础上,将不兼容的接口改为兼容的接口。

     *动机:解决应用环境变化,现存对象在新环境下不兼容的问题。

     *需要解决的问题:

      1>如何解决“迁移的变化”?

      2>如何能利用现有对象的良好实现,又能满足新的应用环境要求的接口?

  *意图:将一个类的接口转换为客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

  *角色:适配器中的几种角色:

    @目标接口(Target): 当前系统业务所期待的接口,可以是抽象类或者接口

    @适配源类(Adaptee): 它是被访问和适配的现存组件库中的组件接口

    @适配者类(Adapter): 它是一个转换器,通过集成或引用适配者的对象,把适配源接口转换成目标接口,让客户按目标接口的格式访问适配者

  *结构:(一般使用对象适配器)

       ——对象适配器:使用组合的方案。

                        

                                 【案例实现】 模拟栈,使用对象适配器实现

                  

                                ——类适配器:使用继承方式,容易混乱。

                                                【案例实现】  模拟栈,用类适配器实现。

                                                   

 3.适配器模式的要点:

      *应用环境:主要应用于“希望复用一些现存的类”,但是接口与复用环境要求不一致。在遗留代码复用,类库迁移等方面非常有用。

      *实现结构:两种实现结构:对象适配器、类适配器。但是类适配器采用“多继承”,会造成不良的高耦合,所以一般使用对象适配器实现。对象适配器采用“对象组合”的方式,更符合松耦合,更利于扩展。

      *使用:使用灵活,不用拘泥于两种结构。例如:可以将适配器模式中的“现存对象”作为新的接口方法参数,来达到适配器的目的。

    *实现:尽可能使用“面向接口”的风格。

4.Bridge桥接模式

            *定义:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。      

*动机:将实现抽象部分与实现部分进行分离,使它们都可以独立地变化。

        *需要解决的问题:

            ——1>如何应对“多维度的变化”?

            ——2>如何利用面向对象的技术使Tank类型可以轻松地沿着“平台”和“型号”两个方向变化,而不引入额外的复杂度?

        *意图:将抽象部分与实现部分分离,让其可以独立地变化。

      *结构:桥接模式主要有以下角色:

      @抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。

      @扩展抽象化(Refined    Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。

      @实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。

      @具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。

 

                       

5.Bridge桥接模式的要点:

  1>使用“对象间的组合关系”来解耦抽象和实现之间固有的绑定关系,使得抽象(Tank的型号)和实现(不同的平台)可以沿着各自的维度变化。

  2>抽象和实现沿着各自维度的变化,就是“子类化”它们,得到各个子类后,便可以进行任意组合,从而获得不同平台上的不同型号。

  3>桥接模式类似于多继承,但桥接模式是比多继承方案更好的解决方法。

  4>一般应用在“两个非常强的变化维度”,有时候当两个变化不会导致纵横交错的结果,不一定要使用桥接模式。

6.Composite组合模式

        *定义:组合模式又称为整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

        *动机:解决客户端对内部实现结构依赖。

        *需要解决的问题:

               ——如何将客户代码与复杂的对象容器结构分离?

               回答:让对象容器自己来实现自身的复杂结构,从而达到客户代码就像处理简单对象一样来处理复杂的对象容器。

         *意图:使用户对单个对象和组合对象的使用具有一致性。

         *结构:客户端依赖于抽象接口,只有这一种依赖关系

       

 7.Composite组合模式的要点:

        *结构:采用树形结构来实现普遍的对象容器,从而将“一对多”关系转化为“一对一”的关系,客户端无需关心处理的是单个对象还是组合的对象容器。

        *核心:将客户代码与复杂的对象容器结构分离,分离后,客户代码将与并非对象容器的复内部实现结构发生依赖关系,从而方便应对变化。

        *方法位置:”Add”和”Remove”等是放置在表示抽象对象的Component类中,还是放置在表示对象容器的Composite类中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。有可能违背“单一职责原则”,但这是应用这种特殊结构的代价。

        *具体实现:可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可以采用缓存技巧(例如:new出来)来改善效率。

8.Decorator装饰模式

         *定义:在不改变现有对象结构的情况下,动态地给该对象增加一些职责(增加额外功能)的模式。

         *动机:解决由于用继承来引入,所以会导致出现多继承,缺乏灵活性的问题。

         *需要面临的问题:

             ——如何使“对象功能的扩展”能够根据需要来动态地实现(在运行时如何实现)?

             ——同时避免“扩展功能的增多”带来的子类膨胀问题?

             ——如何将“功能扩展变化”所导致的影响降为最低?

              *意图:动态地给一个对象增加一些额外的功能。从增加功能来说,装饰者模式比生成子类更加灵活。

              *角色:装饰者中的几种角色:

      @抽象构件(Component): 定义一个抽象接口以规范准备接收附加责任的对象

      @具体构件(ConcreteComponent): 实现抽象构件,通过装饰角色为其添加一些职责

      @抽象装饰(Decorator): 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能  

      @具体装饰(ConcreteDecorator): 实现抽象装饰的相关方法,并给具体构件对象添加附加的责任

          *结构:

                      

8.   Decorator装饰模式的要点:

        *作用:在运行时通过组合来动态地扩展对象功能,并且可以根据需要扩展多个功能。

        *角色:Component类在装饰模式中不应该去实现具体的行为,而且应该透明,透明即无需知道Decorator类,Decorator类是从外部来扩展Component类的功能。

        *实现:装饰者模式在表现上是继承关系,但在实现上是组合关系。可以使用一个或者多个Decorator对象来“装饰”一个Component对象,且装饰后的对象仍然是一个Component对象。

        *核心:解决主体类在多个方向上的扩展功能。

9.Facade外观模式:

         *定义:外观模式又称为 门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式,该模式对外有一个统一的接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。

         *动机:使得子系统更加容易使用。

         *需要解决的问题:

               ——如何简化外部客户程序和系统间的交互接口?

               ——如何将外部客户程序的演化和内部子系统的变化之间的依赖相互解耦?

         *意图:定义一个高层接口,让这个接口使子系统更加容易使用。

         *角色:Facade外观模式的几种角色:

                  @外观(Facade)角色: 为多个子系统对外提供一个共同的接口

        @子系统(SubSystem)角色: 实现系统的部分功能,客户可以通过外观角色访问它

         *结构:   

        

10.Facade外观模式的要点:

       *作用:简化了整个组件系统的接口,同时将组件内部与外部客户程序进行“解耦”。

    *结构:注重架构设计。

    *区别:外观模式注重简化接口,适配器模式注重转换接口,桥接模式注重分离接口(抽象)与实现,装饰模式注重在有稳定接口的前提下,为对象扩展功能。

11.Flyweight享元模式:

       *定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量,避免大量相似对象的开销,从而提高系统资源的利用率。

       *重点:区分应用中的内部状态、外部状态这两种状态,并将外部状态外部化。

       *作用:降低采用面向对象语言编程所带来的运行时代价,尽可能减少内存的使用。

       *需要解决的问题:如何避免大量细粒度对象所带来的开销问题?

       *意图:通过共享技术来支持大量细粒度的对象。

       *结构:

                        

 12. Flyweight享元模式的要点:

        *作用:主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。

        *核心:通过对象共享的做法来降低系统中对象的个数,从而达到降低系统的内存压力。需要注意对象状态的处理。

        *问题:首先评估对象的大小,要根据具体应用情况进行评估,不能凭空想象。

13.Proxy代理模式:

        *定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问,这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

        *动机:实现间接访问使用者或系统结构。

        *需要解决的问题:

              ——如何在不失去透明操作对象的同时来管理/控制这些独享特有的复杂性?

                    回答:增加一层间接层是软件开发中常见的解决方式。

        *意图:为其他对象提供一种代理,用来控制对这个对象的访问。

        *分类:静态代理,动态代理。

        *结构:

            

14. Proxy代理模式的要点:

         *作用:增加一层间接层,来间接访问某些对象。

         *实现:Proxy代理模式的实现方法、实现粒度都相差很大。

         *要求:满足实现间接控制,有时候舍弃透明性也可以接受。


                                                                                                                                 2.UML之物理视图、模型管理视图

1.UML物理视图:

  1>概述:

      UML包括了两种视图来表现实现单元:实现视图和配置视图。

      实现视图:

          <作用>: 显示采用构件、接口以及构件间的依赖,对设计元素(如:类)的实现。

          构件:显示了将可重用的系统片段物理打包成可替代的单元。是用于构造系统的高层次的可重用片段。

      配置视图:

           <作用>:显示了运行时段运算资源的物理分布。

           结点的作用:在运行时,结点可以容纳构件和对象。构件和对象在结点上的分布可以是静态的,也可在结点中转移。

   2>构件:

         定义: 构件是定义了良好接口的物理实现单元,它是系统中可替换的部分。举个例子:电路板上的电容器,独立声卡,独立显卡。

         构件具有它们所支持的接口和所需要其他构件的接口。

         接口:接口是由硬件和软件块支持的操作列表。

         构件视图:显示了构件之间依赖的网络。具有两种形式:一种显示了一系列有效构件(构件库)以及它们的依赖性,系统从他们中被建造。

         显示:用一侧带有两个小长方形的大长方形来表示。可能与代表接口的员连接在一起。

                         

         构件图:显示了构件之间的依赖。如果构件之间的依赖由接口来仲裁,则构件可以用实现相同接口的构件来代替。

         

            带接口构件的一个示例图:

                             

         可替换的构件图:

                               

         3>结点:

             *定义:结点时表示计算资源在运行时的物理对象,通常具有内存和处理能力。

            *显示:用带有结点名称的立方体表示,可以具有分类(可选)。结点之间的关联代表通信路径。关联有用来辨别不同路径的构造型。结点也有泛化关系,将结点的一般描述和具体的特例联系起来。

         

            结点可以容纳对象和构件实例。

            结点中的对象:用嵌套在结点内的对象符号来表示。如果这样表示不方便,对象符号可以包含它所在结点location标签。结点间对象或构件实例间的迁移也可以表示出来。

2.模型管理视图:

       *概述:任何大系统必须划分为较小的单元,以使人们可以在某一时刻与有限的信息工作,使团队中的工作不相互影响。

             模型管理包括包(包含了特殊种类的包)和包之间的依赖关系。

       *包:

             1>定义:包是一部分模型。模型的每个部分必须属于某个包。

             2>重要性:通过系统合理的分解为包,可以极大的提高系统的可维护性。

             3>包含:包包含了顶层的模型元素。每个顶层元素都有一个包,它在这个包中被声明,该包被称作元素的“家”包。

             4>注意:在一个配置好的控制系统中,建模者必须能够对“家”包进行访问可以修改元素的内容,这为大的模型提供了访问控制机制。

             5>功能:包可以用来存储,控制访问,配置管理和构造可重用部件提供了很大的帮助。包之间的依赖关系描述了包的内容之间的依赖关系。

     *包间的依赖关系:依赖关系出现在独立元素之间,但是在任何规模的系统之间,都应该从更高层次观察他们。包之间的依赖关系概述了包中元素的依赖关系,即包间的依赖关系可以从独立元素之间的依赖关系导出。下图展示了一个有依赖关系存在的包结构图:

                                   

        *访问与引入依赖:通常,一个包是不能访问另一个包的内容。包是不透明的,除非它们能被访问或者引入依赖关系。包之间可以相互的访问的形式有两种,第一种就是像正常开发过程中创建包的方式一样,直接放到一起;第二种是引入依赖关系,例如导入依赖包的方式,例如导入jdbc的驱动包。

         *模型和子系统:

               1>定义:模型是从某一个视角观察到的对系统进行完全扫描的包。是从一个视点提供一个系统的封闭的描述。

               2>结构:通常模型为树形结构。根包包含了存在于它体内的嵌套包,嵌套包组成了从给定观点出发的系统的所有细节。子系统是具有单独说明和实现部分的包。它表示具有对系统其它部分存在干净接口的连贯模型单元,通常表示按照一定功能要求或实现要求对系统的划分。模型和子系统都用具有构造型关键字的包表示。

 


谈谈技术原则,技术学习方法,代码阅读及其它

1.选用技术的原则:

     *工作方面:做到能清晰知道对自己有价值的东西是什么,一旦确定后,就积极地进行学习,坚持不懈,勤学笃实,还要多拓展下知识面,要挑实用的技术去学习,切记不可走马观花,浅尝辄止,否则不仅浪费时间,还容易对所学知识造成误解。

     *学习方面:以简单、核心的技术为主。可学可不学的不去学,复杂的在想成为这方面的专家后再去学,时间是宝贵的,不容我们在无意义的事情上浪费时间。

2. 技术学习的技巧:

       *结构

          @重要性:只要学习结构不清楚,无论细节多好,都不算了解。掌握学习结构+学习细节,才能学好这门技术。

          @核心:掌握纵、横两条线。纵线指的是程序执行步骤,需要知道每一个执行步骤具体完成的功能是什么?一般通过源代码中的接口调用来掌握纵线。横线指的是由哪些重要的接口,抽象类的实现,懂得哪个实现是干什么的以及其优缺点,还要把握细节问题(只要愿意多花时间,就能学的熟练)。      

          最有效的学习是什么样的?

           ——当一个技术摆在我们面前,很快就能看出该技术的骨架,优缺点,性能,至少能估计到该技术的大概范围是多大,慢慢提高对技术的悟性,知道其重点和脉络,明确该技术哪些是容易记错、混淆的,哪些是能创新的。

          如何学习设计模式?

          ——需要不断实践,不断重构。不断对自己写的项目、软件提出更高的要求,不断重构,多这样做几遍,对设计模式就很熟悉了。切勿只为了学习模式而去学习模式,每一个模式都针对解决想对应的问题,每一个场景可以采用几种模式实现,深入理解这些问题且知道每种模式及其变种的优缺点才是我们应该做的。

          学习架构所要面临的问题?

          ——1>解决复杂性:思考如何将复杂的程序变得简单?一般做法:封装,常用的封装是OO,还有别的封装方式。注意:类名、方法名要能清晰地看出其身份和功能。

         ——2>解决程序演化与扩展:让程序演化与扩展越简单越好,组合优先继承,怎么暴露API,怎么写文档。

         ——3>性能:如何测试性能,如何评估不同场景中的性能以及缓存机制。

         ——4>功能:和业务有关。

         ——5>易用性

         ——6>纵向扩展,横向拓展,并发...

         ——7>自己开发还是采用第三方插件还是外包以及选择问题。

         学习架构所面临的具体问题?

    1>对象的创建和销毁             2>对象的封装和继承体系   3>对象的粒度和语义划分

    4>对象的复用                        5>对象的测试          6>对象的持久化

    7>具体的API暴露      8>常用Collections                             9>算法问题

    10>性能(与调度有关)              11>回调              12>消灭语义沟

    13>如何将程序变得简单      14>能使用哪些API                             15>对象的管理

    16>异步调用                         17>远程调用

3. 设计软件的最好做法?

       ——设计时懂得取舍,知道目的是什么,目前有什么,应该放弃什么,注重的是如何成功地去开发软件,而不是达到项目需求。  

4.有用的学习方法有哪些?

     拜师学艺:以案例为主进行学习,多看源码,多看现有方案,多写代码。

     左右互博:遇到一个问题,多想想几种解决方法,比较每种方法,从中能获得每种方法的优缺点,设计模式的优缺点。

     庖丁解牛:对自己写的软件进行横、纵向划分,方便了解软件。

     吸量大法:了解软件的演变过程,方便培养自己对技术的预测能力。

5.阅读代码的技巧:

     <1>软件如何工作:找到主要的类和接口,找出主要的调用过程,研究透这个过程。

     <2>软件做什么以及扩展:研究这些主要类和接口的实现/继承。

     <3>了解细节:找出核心算法,对核心算法进行研究,找出核心方法,在脑子中构建出方法间的调用过程,跟着这个方法走一圈,基本就搞懂这个软件了。

 

 

 

 

 

 

posted @ 2021-02-18 08:24  19A4  阅读(151)  评论(1编辑  收藏  举报