zoukankan      html  css  js  c++  java
  • 设计模式(不完全)

    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

    一、设计模式分类

    1. 创建型模式分类

    社会化的分工越来越细,自然在软件设计方面也是如此,因此对象的创建和对象的使用分开也就成为了必然趋势。因为对象的创建会消耗掉系统的很多资源,所以单独对对象的创建进行研究,从而能够高效地创建对象就是创建型模式要探讨的问题。这里有6个具体的创建型模式可供研究,它们分别是:

    • 简单工厂模式
    • 工厂方法模式
    • 抽象工厂模式
    • 创建者模式
    • 原型模式
    • 单例模式

    2. 结构型模式分类

    在解决了对象的创建问题之后,对象的组成以及对象之间的依赖关系就成了开发人员关注的焦点,因为如何设计对象的结构、继承和依赖关系会影响到后续程序的维护性、代码的健壮性、耦合性等。对象结构的设计很容易体现出设计人员水平的高低,这里有7个具体的结构型模式可供研究,它们分别是:

    •  外观模式
    • 适配器模式
    • 代理模式
    • 装饰模式
    • 桥模式
    • 组合模式
    • 享元模式

    3. 行为型模式分类

    在对象的结构和对象的创建问题都解决了之后,就剩下对象的行为问题了,如果对象的行为设计的好,那么对象的行为就会更清晰,它们之间的协作效率就会提高,这里有11个具体的行为型模式可供研究,它们分别是:

    • 模板方法模式(Template Method);
    • 观察者模式(Observer);
    • 状态模式(State);
    • 策略模式(Strategy);
    • 职责链模式(Chain of Responsibility);
    • 命令模式(Command);
    • 访问者模式(Visitor);
    • 调停者模式(Mediator);
    • 备忘录模式(Memento);
    • 迭代器模式(Iterator);
    • 解释器模式(Interpreter)

     二、设计模式的原则

    1、开闭原则(Open Close Principle)

      开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

    2、里氏代换原则(Liskov Substitution Principle)

           里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

    3、依赖倒转原则(Dependence Inversion Principle)

            这个是开闭原则的基础,具体内容:是对接口编程,依赖于抽象而不依赖于具体。

    4、接口隔离原则(Interface Segregation Principle)

            这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

    5、迪米特法则(最少知道原则)(Demeter Principle)

            为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

    6、合成复用原则(Composite Reuse Principle)

     三、常用模式实现

    1. 单例模式

    单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

    比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象

    class Singleton(object):
     2     def __init__(self):
     3         pass
     4 
     5     def __new__(cls, *args, **kwargs):
     6         if not hasattr(Singleton, "_instance"): # 反射
     7             Singleton._instance = object.__new__(cls)
     8         return Singleton._instance
     9 
    10 obj1 = Singleton()
    11 obj2 = Singleton()
    12 print(obj1, obj2) #<__main__.Singleton object at 0x004415F0> <__main__.Singleton object at 0x004415F0>

    通过重写__new__方法,可以实现单例模式,我们可以看到创建的两个实例obj1和obj2是同一个对象。

    2. 工厂模式

    工厂模式是一个在软件开发中用来创建对象的设计模式。

    工厂模式包涵一个超类。这个超类提供一个抽象化的接口来创建一个特定类型的对象,而不是决定哪个对象可以被创建。

    为了实现此方法,需要创建一个工厂类创建并返回。 

    当程序运行输入一个“类型”的时候,需要创建于此相应的对象。这就用到了工厂模式。在如此情形中,实现代码基于工厂模式,可以达到可扩展,可维护的代码。当增加一个新的类型,不在需要修改已存在的类,只增加能够产生新类型的子类。

    简短的说,当以下情形可以使用工厂模式:

    1.不知道用户想要创建什么样的对象

    2.当你想要创建一个可扩展的关联在创建类与支持创建对象的类之间。

    一个例子更能很好的理解以上的内容:

    1. 我们有一个基类Person ,包涵获取名字,性别的方法 。有两个子类male 和female,可以打招呼。还有一个工厂类。
    2.  工厂类有一个方法名getPerson有两个输入参数,名字和性别。
    3.  用户使用工厂类,通过调用getPerson方法。

    在程序运行期间,用户传递性别给工厂,工厂创建一个与性别有关的对象。因此工厂类在运行期,决定了哪个对象应该被创建

    class Person:
        def __init__(self):
            self.name = None
            self.gender = None
    
        def getName(self):
            return self.name
    
        def getGender(self):
            return self.gender
    
    class Male(Person):
        def __init__(self, name):
            print "Hello Mr." + name
    
    class Female(Person):
        def __init__(self, name):
            print "Hello Miss." + name
    
    class Factory:
        def getPerson(self, name, gender):
            if gender == ‘M':
                    return Male(name)
            if gender == 'F':
                return Female(name)
    
    
    if __name__ == '__main__':
        factory = Factory()
        person = factory.getPerson("Chetan", "M")

    3. 建造者模式

     建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节.

     它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。

    主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

    何时使用:一些基本部件不会变,而其组合经常变化的时候。

    如何解决:将变与不变分离开。

    关键代码:建造者:创建和提供实例,指挥者:管理建造出来的实例的依赖关系。

    应用实例: 去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。

    优点: 1、建造者独立,易扩展。 2、便于控制细节风险。

    缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。

    使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。

    注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

    实例:KFC套餐

    建造者模式可以用于描述KFC如何创建套餐:套餐是一个复杂对象,它一般包含主食(如汉堡、鸡肉卷等)和饮料(如果汁、可乐等)等组成部分,不同的套餐有不同的组成部分,而KFC的服务员可以根据顾客的要求,一步一步装配这些组成部分,构造一份完整的套餐,然后返回给顾客。

    #具体产品对象
    class Menu:
        Menu_A=[]
        Menu_B=[]
        def set_MenuA(self,item):
            self.Menu_A.append(item)
        def set_MenuB(self,item):
            self.Menu_B.append(item)
        def get_MenuA(self):
            return self.Menu_A
        def get_MenuB(self):
            return self.Menu_B
    
    # Builder(抽象建造者)
    # 创建一个Product对象的各个部件指定的抽象接口。
    class Product:
        product = Menu()
        def build_hanbao(self):
            pass
        def build_jiroujuan(self):
            pass
        def build_kele(self):
            pass
        def build_shutiao(self):
            pass
    
    # ConcreteBuilder(具体建造者)
    # 实现抽象接口,构建和装配各个部件。
    #套餐A
    class product_A(Product):
        type="A"
        def build_hanbao(self):
            self.hanbao="汉堡"
            self.product.set_MenuA(self.hanbao)
        def build_kele(self):
            self.kele="可乐"
            self.product.set_MenuA(self.kele)
        def getType(self):
            return type
    
    # 套餐B
    class product_B(Product):
        type = "B"
        def build_shutiao(self):
            self.shutiao="薯条"
            self.product.set_MenuB(self.shutiao)
        def build_jiroujuan(self):
            self.jiroujuan="鸡肉卷"
            self.product.set_MenuB(self.jiroujuan)
        def build_kele(self):
            self.kele="可乐"
            self.product.set_MenuB(self.kele)
        def getType(self):
            return type
    
    #Director(指挥者)
    class Make:
        def __init__(self):
            self.builder = None
        def build_product(self, builder):
            self.builder = builder
            print(builder.type)
            if builder.type == "A":
                [step() for step in (builder.build_hanbao,
                builder.build_kele)]
            if builder.type == "B":
                [step() for step in (builder.build_shutiao,
                                     builder.build_jiroujuan,
                                     builder.build_kele)]
    
    #不同类型选择
    def validate_style(builders):
        global valid_input
        try:
            print('套餐A:汉堡、可乐'+'
    '
                  '套装B:薯条、鸡肉卷、可乐')
            product_style = input('请输入您的选择:' )
            builder = builders[product_style]()
            valid_input = True
        except KeyError as err:
            print('Sorry, 没有这个套餐,请重新选择。')
            return (False, None)
        return (True, builder,product_style)
    
    #主函数
    def main():
        builders = dict(A=product_A, B=product_B)
        valid_input = False
        while not valid_input:
            valid_input, builder,product_style = validate_style(builders)
        Waiter = Make()
        Waiter.build_product(builder)
        if product_style == "A":print(builder.product.get_MenuA())
        else:print(builder.product.get_MenuB())
    
    if __name__ =="__main__":
        main()

     4. 原型模式

    用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
    原型模式本质就是克隆对象,所以在对象初始化操作比较复杂的情况下,很实用,能大大降低耗时,提高性能,因为“不用重新初始化对象,而是动态地获得对象运行时的状态”。

    浅拷贝(Shallow Copy):指对象的字段被拷贝,而字段引用的对象不会被拷贝,拷贝的对象和源对象只是名称相同,但是他们共用一个实体。
    深拷贝(deep copy):对对象实例中字段引用的对象也进行拷贝。

    import copy
    from collections import OrderedDict
    
    
    class Book:
        def __init__(self, name, authors, price, **rest):
            '''rest的例子有:出版商、长度、标签、出版日期'''
            self.name = name
            self.authors = authors
            self.price = price  # 单位为美元
            self.__dict__.update(rest)
    
        def __str__(self):
            mylist = []
            ordered = OrderedDict(sorted(self.__dict__.items()))
            for i in ordered.keys():
                mylist.append('{}: {}'.format(i, ordered[i]))
                if i == 'price':
                    mylist.append('$')
                mylist.append('
    ')
                return ''.join(mylist)
    
    
    class Prototype:
        def __init__(self):
            self.objects = dict()
    
        def register(self, identifier, obj):
            self.objects[identifier] = obj
    
        def unregister(self, identifier):
            del self.objects[identifier]
    
        def clone(self, identifier, **attr):
            found = self.objects.get(identifier)
            if not found:
                raise ValueError('Incorrect object identifier: {}'.format(identifier))
            obj = copy.deepcopy(found)
            obj.__dict__.update(attr)
            return obj
    
    
    def main():
        b1 = Book('The C Programming Language', ('Brian W. Kernighan', 'Dennis M.Ritchie'),
                  price=118, publisher='Prentice Hall', length=228, publication_date='1978-02-22',
                  tags=('C', 'programming', 'algorithms', 'data structures'))
        prototype = Prototype()
        cid = 'k&r-first'
        prototype.register(cid, b1)
        b2 = prototype.clone(cid, name='The C Programming Language(ANSI)', price=48.99,
                             length=274, publication_date='1988-04-01', edition=2)
        for i in (b1, b2):
            print(i)
        print("ID b1 : {} != ID b2 : {}".format(id(b1), id(b2)))
    
    
    if __name__ == '__main__':
        main()
    
    """
    >>> python3 prototype.py
    authors: ('Brian W. Kernighan', 'Dennis M. Ritchie')
    length: 228
    name: The C Programming Language
    price: 118$
    publication_date: 1978-02-22
    publisher: Prentice Hall
    tags: ('C', 'programming', 'algorithms', 'data structures')
    
    
    authors: ('Brian W. Kernighan', 'Dennis M. Ritchie')
    edition: 2
    length: 274
    name: The C Programming Language (ANSI)
    price: 48.99$
    publication_date: 1988-04-01
    publisher: Prentice Hall
    tags: ('C', 'programming', 'algorithms', 'data structures')
    
    ID b1 : 140004970829304 != ID b2 : 140004970829472
    """

    5. 适配器模式

    适配器模式(Adapter pattern)(也被称为Wrapper模式)是一种结构型设计模式,帮助我们实现两个不兼容接口之间的兼容。首先,解释一下不兼容接口的真正含义。如果我们希望把一个老组件用于一个新系统中,或者把一个新组件用于一个老系统中,不对代码进行任何修改两者就能够通信的情况很少见。但又并非总是能修改代码,或因为我们无法访问这些代码(例如,组件以外部库的方式提供),或因为修改代码本身就不切实际。在这些情况下,我们可以编写一个额外的代码层,该代码层包含让两个接口之间能够通信需要进行的所有修改。这个代码层就叫做适配器。

    电子商务系统是这方面众所周知的例子。假设我们使用的一个电子商务系统中包含一个calculate_total(order)函数。这个函数计算一个订单的总金额,但货币单位为丹麦克朗(Danish Kroner,DKK)。顾客让我们支持更多的流行货币,比如美元(United States Dollar,USD)和欧元(Euro,EUR),这是很合理的要求。如果我们拥有系统的源代码,那么可以扩展系统,方法是添加一些新函数,将金额从DKK转换成USD,EUR。但是如果应用仅以外部库的方式提供,我们无法访问其源代码,那又该怎么办呢?在这种情况下,我们仍然可以使用这个外部库(例如,调用它的方法),但无法修改/扩展它。解决方案是编写一个包装器(又名适配器)将数据从给定的DKK格式转换成期望的USD或EUR格式。

    适配器模式并不仅仅对数据转换有用。通常来说,如果你想使用一个接口,期望它是function_a(),但仅有function_b()可用,那么可以使用一个适配器把function_b()转换(适配)成function_a()。不仅对于函数可以这样做,对于函数参数也可以如此。其中一个例子是,有一个函数要求参数x、y、z,但你手头只有一带参数x、y的函数。

    6.修饰器模式

    该模式虽名为修饰器,但这并不意味着它应该只用于让产品看起来更漂亮。修饰器模式通常用于扩展一个对象的功能。这类扩展的实际例子有,给枪加一个消音器、使用不同的照相机镜头。

    装饰模式:动态地给一个对象增加一些额外的职责。就扩展功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案。以对客户透明的方式动态地给一个对象附加上更多的责任 可以在不需要创建更多子类的情况下,让对象的功能得以扩展 

    import time
    def timmer(func):
        def deco(*args,**kwargs):
            start_time = time.time()
            func(*args,**kwargs)
            end_time=time.time()
            print('the func run time is %s'%(end_time-start_time))
        return deco
    
    @timmer
    def test1():
        time.sleep(3)
        print('in the test1')
    
    @timmer
    def test2(name):
        time.sleep(1)
        print('test2',name)
    
    test1()
    test2('kobe')
  • 相关阅读:
    匹配session
    Jdk1.8+Eclipse+MySql+Tomcat开发Java应用的环境搭建
    MySQL忘记密码怎么办
    MyBatis框架Maven资源
    MyBatis核心配置文件模版
    MyBatis执行过程显示SQL语句的log4j配置
    MyBatis实体类映射文件模板
    Mybatis 学习-4
    Spring Boot + Swagger
    Spring Boot + Swagger
  • 原文地址:https://www.cnblogs.com/sunny0824/p/13608471.html
Copyright © 2011-2022 走看看