zoukankan      html  css  js  c++  java
  • 面向对象设计模式纵横谈:Prototype 原型模式(笔记记录)

       有一段时间没写东西了,今天继续把没写完的设计模式写完,今天这堂课是创建型设计模式的最后一堂课,原型设计模式,它同样也是解决了对象在创建的过程中的解耦合的情况,面对变化使代码更稳定,更准确的说是使创建对象的主业务逻辑更稳定。好了,我们继续。我们县讨论一下依赖关系。

       依赖关系的倒置

       抽象不应该依赖于实现细节,实现细节应该依赖于抽象。

      -抽象A直接依赖于实现细节b,实现细节b就类似我要吃米饭,上午可能要吃米饭,下午就可能要吃炸酱面,每个人习惯不同,这个是经常变化的。对于软件设计来说,我们可能设计一个我要吃饭的接口,具体吃什么,是米饭还是炸酱面,通过其他方法实现,这样就保证了软件系统接口的稳定性。抽象A和实现细节b的关系就像【人-----》吃米饭】,这样的关系不好,软件易变化,就是容易改代码。这样的关系应该改成【人------》吃饭】,整个环境就安静了,稳定了。



    -抽象A依赖于抽象B,实现细节b依赖于抽象B,这幅图就是【人-----》吃饭】,具体吃什么饭以后再说。



     
    动机(Motivation)

    在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。

    如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
     

    意图(Intent)

    使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象——《设计模式》GoF
    这是原话,很精炼,我们看看类图吧,更明确一点。

    结构(Structure)

    我们看了这么多设计模式的类图了,大家应该也总结出一些经验来,客户端依赖的都是抽象的接口,【这个接口不是C#语言里面的Interface或者抽象类】,原型模式里面有一个抽象原型对象,他是稳定的,通过自身的克隆实现对象的创建。

    例说Prototype应用,代码截图如下:



    上面的代码,GameSystem依赖于具体的new的对象,如果面临对象变化,例如如果要增加一个新的角色,就要重新修改编译了。对此,我们就可以用工厂方法来改变,但是,对于每一个类型都要写一个工厂类,比较繁琐。我们也可以用抽象工厂方法,创建一组对象,如果这样做,这个实现有点笨重,如果增加一个角色面临抽象工厂的剧烈变化。所以我们这里选择使用原型模式。

    先把GameSystem里用到的类型换为抽象类型,这些抽象的类型是面向客户端的,或者说是客户使用的接口。





    再将需要new的具体对象用参数传入,这样在GameSystem这个客户程序里面就只依赖于抽象而不依赖于具体了。具体的NormalActorA、FlyActorA等都不出现在GameSystem中。



    应用程序



    给抽象类增加Clone抽象方法



    给具体类实现Clone方法



    但有一点要注意,MemberwiseClone方法只是一种浅拷贝,它只能拷贝所有的值类型和String,如果是引用类型(例如数组),它就会只拷贝引用,而不会重新创建对象,例如对数组,就只会拷贝数组的地址。

    如下图,左边是栈,右边是堆,(.NET的类都是在堆上)



    如果想深拷贝,除了用笨办法,还可以用序列化的方式来做。首先需要把类标记为可序列化,然后将类序列化到内存,再把内存中的类反序列化,反序列化得到的对象和原来的对象一定是深拷贝。



    在对于结构中的图,可以对应为:Client就是GameSystem,Operation方法就是Run。Prototype抽象类就对应NormalActor,ConcretePrototype即NormalActorA。

     

    Prototype模式的几个要点

    Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有“稳定的接口”。

    Prototype模式对于“如何创建易变类的实体对象”(创建型模式除了Singleton模式以外,都是用于解决创建易变类的实体对象的问题的)采用“原型克隆”的方法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象——所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方不断地Clone。

    Prototype模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法或者序列化来实现深拷贝。

     
    有关创建型模式的讨论

    Singleton模式解决的是实体对象个数的问题。除了Singleton之外,其他创建型模式解决的都是new所带来的耦合关系。

    Factory Method,Abstract Factory,Builder都需要一个额外的工厂类来负责实例化“易变对象”,而Prototype则是通过原型(一个特殊的工厂类)来克隆“易变对象”。(其实原型就是一个特殊的工厂类,它只是把工厂和实体对象耦合在一起了)

    如果遇到“易变类”,起初的设计通常从Factory Method开始,当遇到更多的复杂变化时,再考虑重构为其他三种工厂模式(Abstract Factory,Builder,Prototype)。

     
          一般来说,如果可以使用Factory Method,那么一定可以使用Prototype。但是Prototype的使用情况一般是在类比较容易克隆的条件之上,如果是每个类实现比较简单,都可以只用实现MemberwiseClone,没有引用类型的深拷贝,那么就更适合了。Prototype如果要实现深拷贝,还需要在每个要克隆的类上加序列化标签,这点复杂度要考虑进程序中。

  • 相关阅读:
    【Nginx】ngx_event_core_module模块
    ELMAH--Using HTTP Modules and Handlers to Create Pluggable ASP.NET Components 77 out of 90 rated th
    nyist oj 214 单调递增子序列(二) (动态规划经典)
    java 入门书籍(java7)
    ARCGIS将WGS84坐标投影到高斯平面
    【linux】linux下对java程序生成dump文件,并使用IBM Heap Analyzer进行分析,查找定位内存泄漏的问题代码
    【springboot】【socket】spring boot整合socket,实现服务器端两种消息推送
    【linux】linux修改open file 大小
    【docker】docker限制日志文件大小的方法+查看日志文件的方法
    【docker】docker部署spring boot服务,但是docker logs查看容器输出控制台日志,没有日志打印,日志未打印,docker logs不打印容器日志
  • 原文地址:https://www.cnblogs.com/PatrickLiu/p/6518873.html
Copyright © 2011-2022 走看看