
最近试着读Design pattern, 不过有些概念实在太抽象了, 整理一下自己所学抽象工厂的精神,就是要有abstract class(not implement),而所有不同种类的对象,都是继承这个abstract class,但是使用者只知道interface的接口就好。
很抽象吧,直接举例会比较实在,今天我开发了一个Qt windows app,Qt是跨平台的框架,所以Qt假设里面有个button class
class Button:
def clicked():
print(clicked)
这个Button绝对不会这么简单,他还要跟不同操作系统兼容,所以就会有mac的button,windows的button,我们就可以先开个抽象类
- class Button(ABC):
- @abstractmethod
- def clicked(self):
- pass
- class WinButton(Button):
- def clicked(self):
- print('click winbutton')
- class MacButton(Button):
- def clicked(self): print('click macbutton')
qt的interface可是非常多对象的,有button还有checkbox等等,这样我写程序的时候就要打self.btn = WinButton()这样子???,然后今天要打包到Mac上,又要改成self.btn = MacButton(),这样子是非常没有效率的...,能不能把这个作业系统的环境提取出来呢???
白话就是,我们需要一个工厂,这个工厂可以帮我们呼叫下面不同的控件,例如WinFactory就是要给我windows的控件
- class GUIFactory(ABC):
- @abstractmethod
- def create_button(self):
- pass
- @abstractmethod
- def create_checkbox(self):
- pass
-
- class WinFactory(GUIFactory):
- def create_button(self):
- return WinButton()
- def create_checkbox(self):
- return WinCheckBox()
-
- class MacFactory(GUIFactory):
- def create_button(self):
- return MacButton()
- def create_checkbox(self):
- return MacCheckBox()
换个譬喻,你今天要吃大餐,而有前菜,主餐,甜点,而大餐有不同风格,你不会想要这样说: 主厨我要日式前菜,日式主餐,日式甜点。而是会想要这样说: 日式主厨,我要前菜、主餐、甜点。这边的日式主厨就是工厂啦!!主厨要会煮菜 上菜 切菜 等 这个就是抽象工厂,而日式主厨,西式主厨,就是工厂。
那这样来看看前面的关系图怎么画

资料源: 抽象工厂设计模式
- from abc import ABC, abstractmethod
- class GUIFactory(ABC):
- @abstractmethod
- def create_button(self):
- pass
- @abstractmethod
- def create_checkbox(self):
- pass
- class WinFactory(GUIFactory):
- def create_button(self):
- return WinButton()
- def create_checkbox(self):
- return WinCheckBox()
- class MacFactory(GUIFactory):
- def create_button(self):
- return MacButton()
- def create_checkbox(self):
- return MacCheckBox()
- class Button(ABC):
- @abstractmethod
- def clicked(self):
- pass
-
- class WinButton(Button):
- def clicked(self):
- print('click winbutton')
-
- class MacButton(Button):
- def clicked(self):
- print('click macbutton')
- class CheckBox(ABC):
- @abstractmethod
- def checked(self):
- pass
-
- class WinCheckBox(CheckBox):
- def checked(self):
- print('check wincheckbox')
- class MacCheckBox(CheckBox):
- def checked(self):
- print('check maccheckbox')
- class Application:
- def __init__(self, factory):
- self.button = factory.create_button()
- self.checkbox = factory.create_checkbox()
-
- if __name__ == "__main__":
- app = Application(MacFactory())
- app.button.clicked()
注意,这样子,对用户而言,就是Application最后call的时候传入引数(Mac or Win Factory),这边甚至能加个if else直接判断当前os。最后那个对用户而言 看到的都是app
app.button, app.checkbox, 早就已经透过工厂创造好啦
抽象工厂模式: 对用户来说单一接口,所有class需要继承abstract class。抽象工厂为你提供了一个接口, 可用于创建每个系列产品的对象。 只要代码通过该接口创建对象, 那么你就不会生成与应用程序已生成的产品类型不一致的产品。
类工厂就是一个在运行时创建类的函数。 即允许创建类时根据情况决定其属性,比如,根据用户输入创建属性。
是一个用于创建并返回类的函数。
使用type创建类如下
- def init(self, name):
- self.name = name
- def eat(self):
- pass
- def go_to_vet(self):
- print "go_to_vet"
- return type('Animal', (object,), {
- '__doc__': 'A class representing an arbitrary animal.',
- '__init__': init,
- 'eat': eat,
- 'go_to_vet': go_to_vet,
- })
这种方式的缺点:
下列示例表示
- def create_animal_class():
- def init(self, name):
- self.name = name
-
- def eat(self):
- pass
-
- def go_to_vet(self):
- print(self.name)
-
-
- return type('Animal', (object,), {
- '__doc__': 'A class representing an arbitrary animal.',
- '__init__': init,
- 'eat': eat,
- 'go_to_vet': go_to_vet,
- })
-
-
- Animal1 = create_animal_class( )
- dog = Animal1("my dog")
- dog.go_to_vet()
- cat = Animal1("my cat")
- cat.go_to_vet()
通过函数调用即可获得一个自定义创建的Animal类。使用class关键字创建效果相同:
- def create_animal_class():
- class Animal(object):
- def init(self, name):
- self.name = name
- def eat(self):
- pass
- def go_to_vet(self):
- print "go_to_vet"
- return Animal
- Animal = create_animal_class()
- print Animal #
- #-*- coding:utf-8 -*-
- def get_credential_class(use_proxy=False, tfa=False):
- if use_proxy:
- keys = ['service_name', 'email_address'] # 通过代理身份验证所要的密匙
- else:
- keys = ['username', 'password']
- if tfa:
- keys.append('tfa_token')
-
-
- class Credential(object):
- expected_keys = set(keys)
-
- def __init__(self, **kwargs):
- if self.expected_keys != set(kwargs.keys()):
- raise ValueError('Keys do not match')
-
- for k, v in kwargs.items():
- setattr(self, k, v)
- return Credential
-
- cred = get_credential_class(0,0)
- print(cred)
运行结果:
- class C(object):
- foo = 'bar'
-
- class I(object):
- def __init__(self):
- self.foo = 'bar'
- print C.foo()
- print I.foo() # AttributeError
-
- c1 = C()
- c2 = C()
-
- c1.foo = 'baz'
- print c1.foo #baz
- print c2.foo # bar
-
- C.foo = 'bacon'
- print c1.foo #baz
- print c2.foo #bacon
-
- print c1.__dict__ # {'foo': 'baz'}
- print c2.__dict__ #{}
print I.foo() # AttributeError原因:
foo作为C的一个实例被实例化,但并不作为I的属性被实例化。
由于直接访问I而不是I的实例,因此__init__函数还没有被允许。
一个对象的属性查找顺序遵循首先查找实例对象自己,然后是类,接着是类的父类。
本质:__dict__属性存储着对象的所有属性(和值)
注意:一些内置的数据类型是没有__dict__属性的:
int,list,dict等这些常用的数据类型是没有__dict__属性的,其实这是可预料的,就算给了它们dict属性也没啥用,毕竟它们只是用来做数据容器的。
属性:指一个对象的数据或者函数
- 属性的访问:通过句话(.)访问属性
- 支持运行中添加和修改属性
字段:类的数据变量,例如:name='scolia'
方法:类里面的函数。可分为:
- 实例方法:第一个参数需要是self,它表示一个具体的实例本身。
- 类方法:用classmethod,它的第一个参数不是self,是cls,它表示这个类本身。类方法是那些并不需要类的实例就可以执行的方法
- 静态方法:用staticmethod,可以无视self,而将这个方法当成一个普通的函数使用
- #-*- coding:utf-8 -*-
- class cls:
- clsvar = 1 #普通字段
- def __init__(self):
- self.insvar = 2
-
- ins1 = cls()
- ins2 = cls()
-
- ins1.clsvar = 20
- print cls.clsvar #输出结果为1
- print ins1.clsvar #输出结果为20
- print ins2.clsvar #输出结果为1
-
- #用类名为类变量重新赋值并打印
- cls.clsvar = 10
- print cls.clsvar #输出结果为10
- print ins1.clsvar #输出结果为20
- print ins2.clsvar #输出结果为10
-
- #这次直接给实例1没有在类中定义的变量赋值
- ins1.x = 11
- print ins1.x #输出结果为11
-
-
- #然后再用类名给类中没有定义的变量赋值
- cls.m = 21
- print cls.m #输出结果为21
-
- #再创建一个实例ins3,然后打印一下ins3的变量
- ins3 = cls()
- print ins3.insvar #输出结果为2
- print ins3.clsvar #输出结果为10
- print ins3.m #输出结果为21
- print ins3.x #报错AttributeErro
- class C(object):
- foo = 'bar'
-
- @classmethod
- def classfoo(cls):
- return cls.foo
-
- print c1.classfoo() #bacon
- print c2.classfoo() #bacon
注意:类方法无法访问实例属性,它们并不需要一个实例,但需要类本身。因此c1.classfoo使用的是类的foo而不是实例c1的foo
使用时机:当你继承一个现有类并且所依赖的类属性必须调整时。类工厂是生成带有重载属性的恰当子类的一种恰当方式。
- class C(object):
- foo = 'bar'
-
- @classmethod
- def classfoo(cls):
- return cls.foo
-
- def create_C_subclass(new_foo):
- class SubC(C):
- foo = new_foo
- return SubC
-
- S = create_C_subclass('spam')
- print S.classfoo() #spam
- E = create_C_subclass('eggs')
- print E.classfoo() #eggs
执行C子类的classfoo类方法创建类的方式返回需要的结果。
让类工厂函数难以使用的一点是类工厂返回的是类而不是类的实例。如果一个实例,则必须调用类工厂函数返回的结果才可以。单例模式是一种只允许一个实例的类模式。
类工厂示例:
- class C(object):
- foo = 'bar'
-
- @classmethod
- def classfoo(cls):
- return cls.foo
-
- def CPrime(new_foo='bar'):
- if new_foo == 'bar':
- return C()
- class SubC(C):
- foo = new_foo
- return SubC
-
- EE = CPrime('bar')
- FF = CPrime('bar1')
- print EE #<__main__.C object at 0x01777CB0>
- print FF #
工厂模式是一个在软件开发中用来创建对象的设计模式。
工厂模式包涵一个超类。这个超类提供一个抽象化的接口来创建一个特定类型的对象,而不是决定哪个对象可以被创建。
为了实现此方法,需要创建一个工厂类并返回所需对象。
当程序运行输入一个“类型”的时候,需要创建于此相应的对象。这就用到了工厂模式。在如此情形中,实现代码基于工厂模式,可以达到可扩展,可维护的代码。当增加一个新的类型,不在需要修改已存在的类,只增加能够产生新类型的子类。
简短的说,当以下情形可以使用工厂模式:
- """
- 工厂模式:
- 根据需求产生对象。
- """
- from typing import Any
-
-
- class Clothes:
- """服装工厂类"""
- def __init__(self,name):
- self.name = name
-
- def create(self):
- pass
-
- class Lovely(Clothes):
-
- def __init__(self, name):
- super().__init__(name)
-
- def create(self):
- print(f"{self.name} 生产 汉服")
-
- class Cool(Clothes):
-
- def __init__(self, name):
- super().__init__(name)
-
- def create(self):
- print(f"{self.name} 生产 酷酷的、帅")
-
- class Lipstick(Clothes):
-
- def __init__(self, name):
- super().__init__(name)
-
- def create(self):
- print(f"{self.name} 生产 死亡芭比粉")
-
- class Shoes(Clothes):
-
- def __init__(self, name):
- super().__init__(name)
-
- def create(self):
- print(f"{self.name}生产 红色高跟鞋")
-
- class Shoes2(Clothes):
- def __init__(self, name):
- super().__init__(name)
-
- def create(self):
- print(f"{self.name}生产 蓝色高跟鞋")
-
- class Requirement:
- """定义一个需求类"""
- @staticmethod
- def creatNeed(need):
- if need == "死亡芭比粉":
- return Lipstick("死亡芭比粉")
- elif need == "酷酷的、帅":
- return Cool("酷酷的、帅")
- elif need == "汉服":
- return Lovely("汉服")
- elif need == "红色高跟鞋":
- return Shoes("红色高跟鞋")
- elif need == "蓝色高跟鞋":
- return Shoes2("蓝色高跟鞋")
-
- # Requirement.creatNeed("死亡芭比粉").create()
- # Requirement.creatNeed("红色高跟鞋").create()
- # Requirement.creatNeed("汉服").create()
-
- with open("create.txt","r",encoding="utf-8")as f_r:
- content = f_r.read()
- # print(content)
- Requirement.creatNeed(content).create()
-
- # class StudentNum:
- # num = 0
- #
- # @classmethod
- # def add_num(cls):
- # cls.num += 1
- #
- # @classmethod
- # def get_num(cls):
- # return cls.num
- #
- # def __new__(cls) -> Any:
- # StudentNum.add_num()
- # return super().__new__(cls)
- #
- #
- # class Student(StudentNum):
- # def __init__(self):
- # self.name = ""
- # a = Student()
- # b = Student()
- # c = Student()
- # print(StudentNum.get_num())