首页 > 解决方案 > 在创建动态子类型时摆脱“if”语句

问题描述

这是一个主要关于设计的问题。

让我们假设我们有一个类,它的作用是通过它正在迭代的一些参数来启动某种类型的不同子类。当__init__方法为每个子类型接收不同的参数时,问题就出现了。有什么方法可以避免if函数内部的语句初始化类只是为了知道要传入哪些参数?也许是一些我不知道的设计模式。或者它是一个糟糕设计的结果?

下面是我的意思的一个例子。请注意其中包含 if...else... 的管理静态方法,如果有更多类型的工人,我们将有更多的 if's,这是我试图避免的。请记住,示例很少,if语句可能要复杂得多。

from abc import ABCMeta


class BaseWorker(metaclass=ABCMeta):
    def work(self):
        pass


class Worker1(BaseWorker):
    def __init__(self, name):
        self.name = name

    def work(self):
        pass


class Worker2(BaseWorker):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def work(self):
        pass


class Manager(object):
    @staticmethod
    def manage(attributes_list):
        for attributes in attributes_list:
            if "age" in attributes:
                w = Worker2(name=attributes["name"], age=attributes["age"])
            else:
                w = Worker1(name=attributes["name"])
            w.work()


if __name__ == '__main__':
    dynamic_attributes = [
        {"name": "davay"},
        {"age": "55", "name": "ok"},
        # and so on...
    ]
    Manager.manage(dynamic_attributes)

理想的解决方案是

    @staticmethod
    def desired_manage(attributes_list):
        for attributes in attributes_list:
            w = worker_factory(attributes)
            w.work()

** 请注意,这worker_factory只是解决此问题的方式的任意名称,并不意味着工厂模式就是要走的路。更不用说,如果我们尝试工厂模式,据我所知,if语句只会移动到那里,它不会解决任何问题。

谢谢!

标签: pythonoopdesign-patterns

解决方案


一种方法是向 Manager 注册每个子类,以及确定是否应实例化子类的可调用对象。

像这样的东西(未经测试):

class Manager(object):

    _registry = []
    _default = None

    @classmethod
    def register(cls, callable, klass):
        cls._registry.append((callable, klass))
        return

    @classmethod
    def register_default(cls, klass):
        cls._default = klass
        return

    @staticmethod
    def manage(attributes_list):
        for attributes in attributes_list:
            for callable, klass in Manager._registry:
                if callable(attributes):
                    w = klass(**attributes)
                    break
        else:
            w = Manager._default(**attrs)
        w.work()


class BaseWorker(metaclass=ABCMeta):
    def work(self):
        pass


class Worker1(BaseWorker):
    def __init__(self, name):
        self.name = name

Manager.register_default(Worker1)  


class Worker2(BaseWorker):
    def __init__(self, name, age):
        self.name = name
        self.age = age 

Manager.register(Worker2, lambda attrs: 'age' in attrs)

这种方法有一些缺点:

  • 条件按插入顺序进行测试,因此您需要确保在更严格的条件('name' in attrs and 'age' in attrs例如'name' in attrs. 如果在不同的模块中定义子类,这可能具有挑战性。(callable, klass)也许为每个可用于对注册表进行排序的元组添加优先级
  • 如果attributes_list很大(和/或有很多子类) ,嵌套循环可能会很慢

推荐阅读