美文网首页
1.17类的设计模式

1.17类的设计模式

作者: Benedict清水 | 来源:发表于2020-07-14 22:35 被阅读0次

    一、抽象父类

    首先看一个例子specialize.py:

    class Super:
        def method(self):
            print('in Super.method')
    
        def delegate(self):
            self.action()
    
    
    class Provider(Super):
        def action(self):
            print('in Provider.action')
    
    
    if __name__ == "__main__":
        x = Provider()
        x.method()
        x.delegate()
    

    %python specialize.py

    in Super.method
    in Provider.action
    

    我们来思考一下,当我们通过Provider实例调用delegate方法时,会发生两次独立的继承搜索:

    1. 在x.delegate调用一开始,Python会搜索Provider实例和类树中更上层的类对象,直到在Super中找到delegate的方法。实例x会照常传给该方法的self参数。
    2. 在Super.delegate方法中,self.action会对self以及它上层的对象发起另一次新的继承搜索。因为self引用了一个Provider的实例,所以action方法会在Provider子类中找到。

    我们从delegate方法的角度来看,这个例子的父类就可以被称为抽象父类,也就是类的部分行为预期由其子类来提供。如果所预期的方法没有在子类中定义,那么当继承搜索失败的python就会引发名称未定义的异常。

    class Super:
        def method(self):
            print('in Super.method')
    
        def delegate(self):
            self.action()
    
    
    class Provider(Super):
        pass
    
    
    if __name__ == "__main__":
        x = Provider()
        x.method()
        x.delegate()
    

    %python specialize.py

    Traceback (most recent call last):
      File "/Users/unity/projects/python-node/specialize.py", line 16, in <module>
        x.delegate()
      File "/Users/unity/projects/python-node/specialize.py", line 6, in delegate
        self.action()
    AttributeError: 'Provider' object has no attribute 'action'
    in Super.method
    

    在python中提供特殊的语法来实现抽象父类(抽象基类)
    Python3的实现:abstract.py

    from abc import ABCMeta, abstractmethod
    
    
    class Super(metaclass=ABCMeta):
        def delegate(self):
            self.action()
        
        @abstractmethod
        def action(self):
            pass
    
    
    class Sub(Super):
        pass
    
    
    if __name__=="__main__":
        x = Sub()
    

    % python abstract.py

    Traceback (most recent call last):
      File "D:/WeChatProjects/pythonCode/simplecode/abstract.py", line 18, in <module>
        x = Sub()
    TypeError: Can't instantiate abstract class Sub with abstract methods action
    

    子类中没有定义action方法就不能进行实例化操作。

    from abc import ABCMeta, abstractmethod
    
    
    class Super(metaclass=ABCMeta):
        def delegate(self):
            self.action()
    
        @abstractmethod
        def action(self):
            pass
    
    
    class Sub(Super):
        def action(self):
            print("sub.action")
    
    
    if __name__=="__main__":
        x = Sub()
        x.delegate()
    

    % python abstract.py

    sub.action
    

    采用这种方式编写代码后,除非子类中的所有抽象方法都已经定义,否则带有抽象方法的类是不能进行实例化的。

    二、OOP设计模式:继承。“is-a”关系。

    故事:开一家披萨店,需要员工为顾客服务、准备食物等。当然,作为一名程序员,我们要高级一点,除了普通员工外决定开发一个机器人来制作披萨,出于礼貌和正确控制,我们决定把机器人做成有薪水成本的功能齐全的员工。
    设计:最通用的类Employee提供共同行为,例如加薪(giveRaise)和打印(__repr__)。员工有两种,所以Employee有两个子类:Chef和Server。这两个子类都会继承work方法来打印更详细的信息。而我们的披萨机器人PizzaRobot是一种chef,也是一种Employee。我们称为“is-a”链:机器人是一个主厨,而主厨是一个员工。
    以下是employees.py文件:

    class Employee:
       def __init__(self, name, salary=0):
           self.name = name
           self.salary = salary
    
       def giveRaise(self, percent):
           self.salary = self.salary + (self.salary * percent)
    
       def work(self):
           print(self.name, "does stuff")
    
       def __repr__(self):
           return "<Employee:name={},salary={}>".format(self.name, self.salary)
    
    
    class Chef(Employee):
       def __init__(self, name):
           Employee.__init__(self, name, 50000)
    
       def work(self):
           print(self.name, "makes food")
    
    
    class Server(Employee):
       def __init__(self,name):
           Employee.__init__(self, name, 40000)
    
       def work(self):
           print(self.name, "interfaces with customer")
    
    
    class PizzaRobot(Chef):
       def __init__(self,name):
           Chef.__init__(self, name)
    
       def work(self):
           print(self.name, "makes pizza")
    
    
    if __name__ == "__main__":
       bob = PizzaRobot("bob")
       print(bob)
       bob.work()
       bob.giveRaise(0.20)
       print(bob)
    

    % python employees.py

    <Employee:name=bob,salary=50000>
    bob makes pizza
    <Employee:name=bob,salary=60000.0>
    

    创建一个名为bob的机器人,打印bob会执行Employee.__repr__方法,而给bob加薪则会运行Employee.giveRaise方法,因为继承会在employee中找到方法。
    从程序员的角度看,继承是由属性点号操作启动的,并由此触发实例、类以及任何父类中的变量名搜索。从类的设计者的角度看,继承是一种指明集合成员关系的方式:类定义了一个属性集合,可由更具体的集合(子类)继承和定制。

    三、OOP设计模式:组合。“has-a”关系

    故事:披萨店是一个组合对象:有一个烤炉,也有服务生和主厨这些员工。当顾客来店里下单时,店里的组件就会开始行动:服务生接单,主厨制作披萨。
    设计:PizzaShop类是容器和控制器。它的构造函数创建并嵌入员工类,以及定义Oven类的实例。
    以下是pizzashop.py文件:

    from employees import PizzaRobot, Server
    
    
    class Customer:
        def __init__(self, name):
            self.name = name
    
        def order(self, server):
            print(self.name, "orders from", server)
    
        def pay(self, server):
            print(self.name, "pays for item to", server)
    
    
    class Oven:
        def bake(self):
            print("oven bakes")
    
    
    class PizzaShop:
        def __init__(self):
            self.server = Server('Pat')
            self.chef = PizzaRobot('Bob')
            self.oven = Oven()
    
        def order(self, name):
            customer = Customer(name)
            customer.order(self.server)
            self.chef.work()
            self.oven.bake()
            customer.pay(self.server)
    
    
    if __name__ == "__main__":
        scene = PizzaShop()
        scene.order("Homer")
        print("...")
        scene.order("shaggy")
    

    % python pizzashop.py

    Homer orders from <Employee:name=Pat,salary=40000>
    Bob makes pizza
    oven bakes
    Homer pays for item to <Employee:name=Pat,salary=40000>
    ...
    shaggy orders from <Employee:name=Pat,salary=40000>
    Bob makes pizza
    oven bakes
    shaggy pays for item to <Employee:name=Pat,salary=40000>
    

    在测试程序中,调用PizzaShop的order方法时,内嵌对象会按照顺序进行工作。每份工作订单都会创建一个新的Customer对象,并且把内嵌的Server对象传给Customer的方法。虽然顾客是流动的,但是服务员是披萨店的组件。
    从程序员的角度看,组合涉及把其他对象嵌入容器对象内,并促使其实现容器方法。对类的设计者来说,组合是在一个问题领域中另一呈现关系的方式。但是,组合不是集合的成员关系,而是组件,也是整体的组成部分。

    四、OOP设计模式:委托。“包装器”代理对象

    委托通常是指控器对象内嵌其他对象,并把操作请求传递那些内嵌的对象。控制器能够负责管理类活动,例如记录日志和验证访问,为接口组件添加额外步骤,或监视活跃实例。
    在python中,通常使用__getattr__方法钩子来实现委托。因为这个运算符重载方法会拦截对不存在属性的访问,所以包装器类可以使用__getattr__方法来把任意的访问转发给被包装的对象。因为该方法允许一般性地路由属性请求,所以包装器类保有被包装对象的接口,并可以添加其他的附加操作。

    class Wrapper:
        def __init__(self, object):
            self.wrapped = object
    
        def __getattr__(self, attrname):
            print("Trace:" + attrname)
            return getattr(self.wrapped, attrname)
    
    
    if __name__ == "__main__":
        x = Wrapper([1, 2, 3])
        x.append(4)
        print(x.wrapped)
    

    % python trace.py

    Trace:append
    [1, 2, 3, 4]
    

    对x.append(4)操作会被__getattr__拦截,getattr内置函数——通过名称字符串获取被包装对象的属性:getattr(X,N)就像X.N,只不过这里N时在运行时求值为字符串的表达式,而不是变量。

    小结

    委托:把对象包装在代理类内
    组合:控制内嵌的对象
    继承:从其他类中获取行为

    相关文章

      网友评论

          本文标题:1.17类的设计模式

          本文链接:https://www.haomeiwen.com/subject/ohktqktx.html