zoukankan      html  css  js  c++  java
  • [Python设计模式] 第8章 学习雷锋好榜样——工厂方法模式

    github地址:https://github.com/cheesezh/python_design_patterns

    简单工厂模式 v.s. 工厂方法模式

    以简单计算器为例,对比一下简单工厂模式和工厂方法模式的区别。

    简单工厂模式

    from abc import ABCMeta, abstractmethod
    
    
    class Operation():
        """
        抽象产品类(运算符类)
        """
        __metaclass__ = ABCMeta
    
        def __init__(self):
            self.result = None
    
        @abstractmethod
        def get_result(self):
            pass
    
        
    class AddOperation(Operation):
        """
        具体产品类(加法运算符)
        """
        def get_result(self, number_a, number_b):
            self.result = number_a + number_b
            return self.result
    
        
    class SubOperation(Operation):
        """
        具体产品类(减法运算符)
        """
        def get_result(self, number_a, number_b):
            self.result = number_a - number_b
            return self.result
    
        
    class MulOperation(Operation):
        """
        具体产品类(乘法运算符)
        """
        def get_result(self, number_a, number_b):
            self.result = number_a * number_b
            return self.result
        
        
    class DivOperation(Operation):
        """
        具体产品类(除法运算符)
        """
        def get_result(self, number_a, number_b):
            if number_b == 0:
                print("With operator '/', the second number can not be zero.")
                return self.result
            self.result = number_a / number_b
            return self.result
        
        
    class OperationFactory():
        """
        产品工厂类
        """
        @classmethod
        def create_operate(self, operator):
            oper = None
            if operator == "+":
                oper = AddOperation()
            elif operator == "-":
                oper = SubOperation()
            elif operator == "*":
                oper = MulOperation()
            elif operator == "/":
                oper = DivOperation()
            else:
                print("Wrong operator.")
            return oper
    
    number_a = int(input("input a number:"))
    operator = str(input("input a operater(+ - * /):"))
    number_b = int(input("input a number:"))
    
    oper = OperationFactory.create_operate(operator)
    print(oper.get_result(number_a, number_b))
    
    input a number:99
    input a operater(+ - * /):/
    input a number:9
    11.0
    

    工厂方法模式

    from abc import ABCMeta, abstractmethod
    
    
    class IFactory():
        """
        通用工厂接口
        """
        __metaclass__ = ABCMeta
        
        @abstractmethod
        def create_operation(self):
            pass
    
        
    class AddFactory(IFactory):
        """
        实现工厂接口的加法工厂类
        """
        def create_operation(self):
            return AddOperation()
    
        
    class SubFactory(IFactory):
        """
        实现工厂接口的剑法工厂类
        """
        def create_operation(self):
            return SubOperation()
    
        
    class MulFactory(IFactory):
        """
        实现工厂接口的乘法工厂类
        """
        def create_operation(self):
            return MulOperation()
        
    
    class DivFactory(IFactory):
        """
        实现工厂接口的除法工厂类
        """
        def create_operation(self):
            return DivOperation()
        
    
    def main():
        number_a = int(input("input a number:"))
        operator = str(input("input a operater(+ - * /):"))
        number_b = int(input("input a number:"))
    
        if operator == "+":
            oper_factory = AddFactory()
        elif operator == "-":
            oper_factory = SubFactory()
        elif operator == "*":
            oper_factory = MulFactory()
        elif operator == "/":
            oper_factory = DivFactory()
        else:
            print("Wrong operator.")
    
        oper = oper_factory.create_operation()
        print(oper.get_result(number_a, number_b))
        
    main()
    
    input a number:99
    input a operater(+ - * /):/
    input a number:11
    9.0
    

    点评

    工厂方法更复杂了?

    如果需要增加其他运算,比如求M的N次方。

    在简单工厂模式里,先增加一个求M的N次方的产品类,然后更改工厂类的if判断增加分支即可。

    在工厂方法模式里,先增加一个求M的N次方的产品类,还要新增一个相关工厂类,最后还有修改客户端代码。

    这就是简单工厂和工厂方法的区别所在。简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关类,对于客户端来说,去除了与具体产品的依赖。但是,如果要增加一个新的功能,比如求M的N次方,需要更改工厂类的if判断分支条件,修改原有的类?违背了开放-封闭原则,这可不是好方法。所以就需要工厂方法模式来处理。

    工厂方法模式

    工厂方法模式,定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类[DP]。

    相当于将简单工厂中的工厂类,变成了一个工厂抽象接口和多个具体生成对象的工厂,于是我们要增加求M的N次方的功能,就不需要更改工厂类,只需要增加此功能的运算类和相应的工厂类即可。这样整个工厂和产品体系其实都没有修改,而只是扩展,这就完全符合了开放-封闭原则。

    但是,工厂方法模式是现实,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行。要增加新功能,本来修改工厂类,现在修改客户端了。

    题目

    木叶学校组织学雷锋活动,让鸣人,小樱,佐助帮敬老院的老人扫地,洗衣,买米,如何实现?

    class LeiFeng():
        
        def sweep(self):
            print("扫地")
        
        def wash(self):
            print("洗衣")
        
        def buy_rice(self):
            print("买米")
            
    class Student(LeiFeng):
        pass
    
    def main():
        mingren = Student()
        xiaoying = Student()
        zuozhu = Student()
        
        mingren.sweep()
        xiaoying.wash()
        zuozhu.buy_rice()
    main()
    
    扫地
    洗衣
    买米
    

    点评

    • 学生都会毕业,但是帮助老人是长期工作,所以每次不同的人帮助老人,都需要改客户端代码,而且老人不可能知道所有来帮忙的学生的名字;
    • 除了学生,社区志愿者也可以帮助老人

    如何用简单工厂方法解决上述问题?

    class Volunteer(LeiFeng):
        pass
    
    
    class SimpleFactory():
        
        @classmethod
        def create_leifeng(self, leifeng_type):
            self.leifeng = None
            if leifeng_type == "学生":
                self.leifeng = Student()
            elif leifeng_type == "志愿者":
                self.leifeng = Volunteer()
            else:
                print("ERROR LeiFeng Type")
            return self.leifeng
        
    def main():
        studentA = SimpleFactory.create_leifeng("学生")
        studentA.buy_rice()
        studentB = SimpleFactory.create_leifeng("学生")
        studentB.wash()
        studentC = SimpleFactory.create_leifeng("学生")
        studentB.sweep()
        
    main()
    
    买米
    洗衣
    扫地
    

    点评

    • 好的地方,客户端的代码,如果要换志愿者,只需要换参数即可;
    • 坏的地方,在任何实例化的时候都需要写一句SimpleFactory.create_leifeng("学生"),这会导致大量重复,在修改为志愿者的时候非常麻烦,可以用工厂方法解决这个问题;
    from abc import ABCMeta, abstractmethod
    
    
    class ILeiFengFactory():
        __metaclass__ = ABCMeta
        
        @abstractmethod
        def create_leifeng(self):
            pass
    
        
    class StudentFactory(ILeiFengFactory):
        
        def create_leifeng(self):
            return Student()
    
        
    class VolunteerFactory(ILeiFengFactory):
        
        def create_leifeng(self):
            return Volunteer()
    
    def main():
        leifeng_factory = StudentFactory()
        stu1 = leifeng_factory.create_leifeng()
        stu2 = leifeng_factory.create_leifeng()
        stu3 = leifeng_factory.create_leifeng()
        
        stu1.sweep()
        stu2.wash()
        stu3.buy_rice()
        
    main()
    
    扫地
    洗衣
    买米
    

    点评

    • 此时如果要将学生改成志愿者,只需要修改一行代码即可;
    • 工厂方法克服了简单工厂违背开放-封闭原则的缺点,又保持了封装对象创建过程的优点;

    总结

    简单工厂和工厂方法都是集中封装了对象的创建,使得要更换对象时,不需要做大的改动就可以实现,降低了客户程序和产品对象的耦合。

    工厂方法是简单工厂模式的进一步抽象和推广,由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。但缺点是由于每加一个产品,就需要加一个产品工厂类,增加了额外开发量。

    另外,工厂方法还是没有避免修改客户端的代码,可以利用反射解决避免分支判断的问题。

  • 相关阅读:
    Vue 过滤器入门
    Vue 常用指令
    git 报错 error: failed to push some refs to .....
    CSS 小结笔记之文字溢出处理
    CSS 小结笔记之元素的隐藏与显示
    CSS 小结笔记之定位
    CSS 小结笔记之清除浮动
    CSS 小结笔记之浮动
    CSS 小结笔记之盒子模型
    CSS 实例之文字的凸起与凹陷
  • 原文地址:https://www.cnblogs.com/CheeseZH/p/9392938.html
Copyright © 2011-2022 走看看