python 设计模式 02

  |  

重载(Overload)和重写(Override)

重载(Overload)

解释

重载是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

方法重载的好处就是让类以统一的方式处理不同类型的一种手段,调用方法时通过传递给他们的不同个数和类型的参数来决定具体使用哪个方法,这就是多态性。

它的特点是:重载发生在本类,方法名相同,参数列表不同,与返回值无关,只和方法名,参数的类型相关。

举例

没有重载前,不同的参数的方法需要定义不同的名字防止冲突(JAVA示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestMath {

public int sumWithTwoNum(int num1, int num2) {
return num1 + num2;
}

public int sumWithThreeNum(int num1, int num2, int num3) {
return num1 + num2 + num3;
}

public int sumWithFourNum(int num1, int num2, int num3, int num4) {
return num1 + num2 + num3 + num4;
}
}

重载后可以定义为同一个名字的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestMath {

public int sum(int num1, int num2) {
return num1 + num2;
}

public int sum(int num1, int num2, int num3) {
return num1 + num2 + num3;
}

public int sum(int num1, int num2, int num3, int num4) {
return num1 + num2 + num3 + num4;
}
}

java重载有一些规则,比如仅仅是返回值类型不同和修饰符不同都不构成方法的重载,仅仅是方法参数的名称不同也不构成重载。

主要是介绍python,只介绍重载定义,具体重载规则请自行百度学习。

重写(Override)

重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写。

重写是发生在类的继承关系,或者类的实现关系中的,重写后的方法和原方法需要保持完全相同的返回值类型、方法名、参数个数以及参数类型,简单来说,就是子类重写的方法必须和父类保持完全一致,说白了就是多态。

比如:定义了一个动物类,其中有一个进食的方法,动物类有两个子类:猫类和狗类,猫类应该对这个进食的方法重写,具体为:猫吃鱼,而狗类则应重写为:狗啃骨头。因为猫和狗进食的具体内容都不同,所以需要重写。

多态

不同对象调用父类的同一方法产生不同的结果。调用不同的子类将会产生不同的行为,而无须明确知道这个子类实际上是什么,这是多态的重要应用场景。多态的表现形式之一就是继承,先抽象,再继承。

鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由”当前方法和属性的集合”决定。

鸭子类型这一名字出自美国James Whitcomb Riley(有的材料说他是诗人,有的说他是测试人员)提出的如下的表述:

When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.

当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Duck:
def walk(self):
print("Walk like a duck")

def quack(self):
print("Quack like a duck")

def attack(self):
print("Attack like a duck")


# 可达鸭
class MallardDuck(Duck):
def walk(self):
print("MallardDuck walking")

def quack(self):
print("MallardDuck quacking")

def attack(self):
print("MallardDuck attacking")


# 大葱鸭
class FarfetchdDuck(Duck):
def walk(self):
print("FarfetchdDuck walking")

def quack(self):
print("FarfetchdDuck quacking")

def attack(self):
print("FarfetchdDuck attacking")


# 和火箭队战斗
def fight_with_team_rocket(duck: Duck):
duck.walk()
duck.quack()
duck.attack()


if __name__ == '__main__':
mallard_duck = MallardDuck()
fight_with_team_rocket(mallard_duck)
farfetchd_duck = FarfetchdDuck()
fight_with_team_rocket(farfetchd_duck)

C# 设计模式

继承分为「接口继承」和「实现继承」。在编程语言或者 framework 中实现「接口继承」比较容易,一般保证一定的二进制接口兼容,或者一定的命名规范即可(比如 SICP 中的查表 data-driven)。著名的 Microsoft Component Object Model 和 CORBA 只有接口继承。实现「实现继承」则相对复杂。一般需要在编译级别支持,所以多为语言本身支持。拥有「实现继承」的语言可以通过通过自限的方式模拟「接口继承」。C++ 实现了完全的「多实现继承」,所以它可以完全通过自限的方式模拟「多接口继承」。Java 和 C# 只有「单实现继承」,如果通过自限的方式只能支持「单接口继承」,所以 Java 和 C# 引入了特别的 interface。

作者:冯东
链接:https://www.zhihu.com/question/20685467/answer/15848984
来源:知乎

根据以上信息可以知道,再Java或者C#中,实现设计模式都是通过接口的方式进行设计。

结构型模式

适配器模式

内容

将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

两种实现方式:

  1. 类适配器:使用多继承
  2. 对象适配器:使用组合

角色:

  • 目标接口(Target)
  • 待适配的类(Adaptee)
  • 适配器(Adapter)

适用场景

  1. 想使用一个已经存在的类,而它的接口不符合你的要求
    2.(对象适配器)想使用一些已经存在的子类,但不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。

实现

类适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from abc import ABC, abstractmethod


class AlgorithmInterface(ABC):
@abstractmethod
def run(self):
pass


class ColMap(AlgorithmInterface):
def __init__(self, params):
self.params = params

def run(self):
print(f"I am running with params: {self.params}")


class TorchNgp:
def view(self):
print("I am running with no params")


# 类适配器
class TorchNgpAdapter(AlgorithmInterface, TorchNgp):
def run(self):
self.view()


# web点击算法发起任务
def web_show_and_click(algorithm):
algorithm.run()


if __name__ == '__main__':
colmap = ColMap("params")
web_show_and_click(colmap)

torch_ngp = TorchNgpAdapter()
web_show_and_click(torch_ngp)

对象适配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from abc import ABC, abstractmethod


class AlgorithmInterface(ABC):
@abstractmethod
def run(self):
pass


class ColMap(AlgorithmInterface):
def __init__(self, params):
self.params = params

def run(self):
print(f"I am running with params: {self.params}")


class TorchNgp:
def view(self):
print("I am running with no params")


# 类适配器
class AlgorithmAdapter(AlgorithmInterface):
def __init__(self, algorithm):
self.algorithm = algorithm

def run(self):
if hasattr(self.algorithm, "run"):
self.algorithm.run()
elif hasattr(self.algorithm, "view"):
self.algorithm.view()
else:
raise NotImplementedError


# web点击算法发起任务
def web_show_and_click(algorithm):
algorithm.run()


if __name__ == '__main__':
col_map = ColMap("params")
torch_ngp = TorchNgp()
web_show_and_click(AlgorithmAdapter(col_map))
web_show_and_click(AlgorithmAdapter(torch_ngp))

扩展

聚合(aggregation):指的是整体与部分的关系。通常在定义一个整体类后,再去分析这个整体类的组成结构。从而找出一些组成类,该整体类和组成类之间就形成了聚合关系。需求描述中“包含”、“组成”、“分为…部分”等词常意味着聚合关系。

简单的说就是类中使用的实例是外部传入的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Cpu:
def show(self):
print("I am cpu")


class Computer:
def __init__(self, cpu):
self.cpu = cpu

def has_cpu(self):
self.cpu.show()


cpu = Cpu()
computer = Computer(cpu)
computer.has_cpu()

组合(composition):也表示类之间整体和部分的关系,但是组合关系中部分和整体具有统一的生存期。一旦整体对象不存在,部分对象也将不存在。部分对象与整体对象之间具有共生死的关系。

简单的说就是使用的实例是类中生成的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Cpu:
def show(self):
print("I am cpu")


class Computer:
def __init__(self):
self.cpu = Cpu()

def has_cpu(self):
self.cpu.show()


computer = Computer()
computer.has_cpu()

桥模式

内容

将一个事物的两个维度分离,使其都可以独立地变化

角色

  • 抽象(Abstraction)
  • 细化抽象(RefinedAbstraction)
  • 实现者(Implementor)
  • 具体实现者(ConcreteImplementor

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from abc import ABC, abstractmethod


# 实现者
class GL(ABC):
def __init__(self, loader):
self.loader = loader

@abstractmethod
def screen(self):
pass


# 抽象
class Loader(ABC):
@abstractmethod
def loader(self):
pass


# 细化抽象
class PlyLoader(Loader):
def loader(self):
print("ply loader")


class ObjLoader(Loader):
def loader(self):
print("obj loader")


# 具体实现者
class WebGL(GL):
def screen(self):
self.loader.loader()
print("call webgl screen method")


class OpenGL(GL):
def screen(self):
self.loader.loader()
print("call opengl screen method")


if __name__ == '__main__':
webgl = WebGL(PlyLoader())
webgl.screen()
opengl = OpenGL(ObjLoader())
opengl.screen()

这样之后即使再增加其他模型的加在类或者其他类型的GL方式都可以方便的添加。

应用场景

当事物有两个维度上的表现,两个维度都可能扩展时。

优点

  • 抽象和实现相分离
  • 优秀的扩展能力

组合模式

内容

将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

角色

  • 抽象组件(Component)
  • 叶子组件(Leaf)
  • 复合组件(Composite)
  • 客户端(Client)

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from abc import ABC, abstractmethod


class AlgorithmInterface(ABC):
@abstractmethod
def train_model(self):
pass


class ColMapApp(AlgorithmInterface):
def __init__(self, params):
self.params = params

def train_model(self):
print(f"ColMapApp am training with params: {self.params}")


class TorchNgpApp(AlgorithmInterface):
def train_model(self):
print(f"TorchNgpApp am training with no params")


class NewBeeApp(AlgorithmInterface):
def __init__(self, algorithms):
self.algorithms = []

for algorithm in algorithms:
self.add_algorithm(algorithm)

def add_algorithm(self, algorithm):
self.algorithms.append(algorithm)

def train_model(self):
print("###### NewBeeApp Training ######")
for algorithm in self.algorithms:
algorithm.train_model()
print("###### NewBeeApp Trained ######")


# web点击算法发起任务
def web_show_and_click(algorithm):
algorithm.train_model()


if __name__ == '__main__':
colmap_app = ColMapApp(params="colmap_params")
torch_ngp_app = TorchNgpApp()
new_bee_app = NewBeeApp(algorithms=[colmap_app, torch_ngp_app])

# 单个算法发起任务
web_show_and_click(colmap_app)
# 组合算法发起任务,调用方式相同
web_show_and_click(new_bee_app)

适用场景

  1. 表示对象的“部分-整体”层次结构(特别是结构是递归的)
  2. 希望用户忽略组合对象与单个对象的不同,用户统一地使用组合结构中的所有对象

优点

  • 定义了包含基本对象和组合对象的类层次结构
  • 简化客户端代码,即客户端可以一致地使用组合对象和单个对象
  • 更容易增加新类型的组件

装饰模式

内容

装饰模式是在原有对象的基础上,动态增加一个新的特性。与继承的最大区别是,这种新增特性是比较泛化的,可以被多种对象增加。

python中通常用的是通过函数装饰器或者类装饰器,直接对对象所属的类进行装饰,语言特性已包含,因此此设计模式略过。

外观模式

内容

外观模式又被称为门面模式,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

角色

  • 外观(facade)
  • 子系统类(subsystem classes)

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class LoaderModel:
def loader(self):
print("loader model")


class DrawScreen:
def draw(self):
print("draw screen")


class LoadAnimation:
def animation(self):
print("load animation")


class Page:
def __init__(self):
self.loader = LoaderModel()
self.draw = DrawScreen()
self.animation = LoadAnimation()

def load(self):
self.loader.loader()
self.draw.draw()
self.animation.animation()


if __name__ == '__main__':
page = Page()
page.load()

使用场景

一个例子是比如维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展,但是它包含很重要的功能,新的开发必须依赖于它,这样增加外观Facade类,为系统封装一个比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。

优点

  • 减少系统相互依赖
  • 提高了灵活性
  • 提高了安全性

享元模式(Flyweight)

内容

  • 图形图像处理软件:在图形图像处理软件中,可能需要创建大量的形状对象,如矩形、圆形等。通过使用享元模式,可以共享形状对象的属性,从而减少内存使用和提高性能。
  • 游戏开发:在游戏开发中,可能需要创建大量的游戏角色、武器等对象。通过使用享元模式,可以共享游戏对象的属性,从而减少内存使用和提高性能。
  • 网络服务器:在网络服务器中,可能需要创建大量的网络连接对象。通过使用享元模式,可以共享网络连接对象的属性,从而减少内存使用和提高性能。
  • 数据库连接池:在数据库连接池中,可能需要创建大量的数据库连接对象。通过使用享元模式,可以共享数据库连接对象的属性,从而减少内存使用和提高性能。

角色:

  • 抽象享元角色(Flyweight)
  • 具体享元角色(Concrete Flyweight)
  • 非享元角色(Unsharable Flyweight)
  • 享元工厂角色(Flyweight Factory)

适用场景

  1. 想使用一个已经存在的类,而它的接口不符合你的要求
    2.(对象适配器)想使用一些已经存在的子类,但不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
from abc import ABC, abstractmethod


class Flyweight(ABC):
def __init__(self, shared_state):
self.shared_state = shared_state
print(f"Flyweight: ({self.shared_state})")

@abstractmethod
def show(self, unique_state):
pass


class ConcreteFlyweight(Flyweight):
def show(self, unique_state):
print(f"ConcreteFlyweight: ({self.shared_state}, {unique_state})")


class FlyweightFactory:
_flyweights = {}

def __init__(self, shared_states):
for shared_state in shared_states:
self._flyweights[str(shared_state)] = ConcreteFlyweight(shared_state)

def get_flyweight(self, shared_state):
key = str(shared_state)
if key not in self._flyweights:
self._flyweights[key] = ConcreteFlyweight(shared_state)
return self._flyweights[key]


# 显示模型的客户端代码
class Client:
def __init__(self, factory):
self.factory = factory

def screen(self, model, position):
flyweight = self.factory.get_flyweight(model)
flyweight.show(position)


# 这里定义模型类,load_model在FlyweightFactory中的初始化时调用
class Model:
def __init__(self, name):
self.name = name

def __str__(self):
return self.name


if __name__ == '__main__':
# 初始化模型
roma = Model("罗马斗兽场")
lion = Model("狮子")
wolf = Model("狼")
bird = Model("鸟")
car = Model("汽车")

# 客户端代码, 初始化时加载模型
client = Client(FlyweightFactory([roma, lion, bird, car]))

# 显示模型
client.screen(roma, (0, 0, 0))
client.screen(lion, (1, 1, 1))
client.screen(wolf, (2, 2, 2))
client.screen(bird, (3, 3, 3))
client.screen(car, (4, 4, 4))
client.screen(lion, (5, 5, 5))
client.screen(wolf, (6, 6, 6))
client.screen(bird, (7, 7, 7))

优点

相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。

缺点

1.为了共享对象,需要将不能共享的状态外部化,会增加程序的复杂性

2.对享元模式的外部状态会增长运行时间

代理模式

内容

为其他对象提供一种代理,以此控制一个对象的访问方式。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

角色:

  • 抽象角色(Subject)
  • 真实角色(Real Subject)
  • 代理角色(Proxy)

适用场景

  • 远程代理:隐藏对象位于远程地址空间的事实。例如写入数据,而数据库不在本地,需要远程访问。隐藏数据库不在本地的事实,使用代理进行远程存储。
  • 虚代理:为了节省资源开销,在功能没有被真正调用时,就不运行。例如浏览器中的无图模式,不显示图片,等你真正需要查看某张时,点击才会真正加载图像,不点击时,只会告诉你,此处是一张图像,并不显示
  • 保护代理:访问一个对象时有一些附加的内务处理。例如为了区分用户权限而设置的代理,普通用户只有访问权限,而开发人员有写入权限。

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
from abc import ABC, abstractmethod


class Subject(ABC):
def __init__(self):
self.model = None

@abstractmethod
def load(self, model, *args):
pass

@abstractmethod
def show(self):
pass


class RealSubject(Subject):
def load(self, model, *args):
if not self.model:
print(f"本地模型 {model} 加载中...")
self.model = model
print(f"{model} 模型加载完成!")

def show(self):
print(f"{self.model} 模型展示!")


class RemoteProxy(Subject):
def __init__(self):
super().__init__()
self.subject = RealSubject()

def load(self, model, *args):
print("远程模型加载中...")
self.subject.model = model
self.subject.load(model)

def show(self):
return self.subject.show()


class VirtualProxy(Subject):
def __init__(self):
super().__init__()
self.subject = RemoteProxy()

def load(self, model, *args):
self.model = model
print("虚拟模型已经记录...")

def show(self):
if not self.model:
print("虚拟模型未记录!")
return
print("虚拟模型展示!")
self.subject.load(self.model)
self.subject.show()


class ProtectionProxy(Subject):
def __init__(self):
super().__init__()
self.subject = VirtualProxy()

def load(self, model, *args):
user = args[0]
if user == "admin":
self.subject.load(model)
self.model = model
else:
print("权限不足!")

def show(self):
if not self.model:
print("模型未加载!")
return
self.subject.show()


def client_code(models_and_users):
proxy_list = []

for user, model in models_and_users:
# proxy = RealSubject()
# proxy = RemoteProxy()
# proxy = VirtualProxy()
proxy = ProtectionProxy()
proxy.load(model, user)
proxy_list.append(proxy)

for proxy in proxy_list:
proxy.show()


if __name__ == "__main__":
model_and_user = [
("admin", "roma"),
("user", "bird"),
("admin", "car")
]
client_code(model_and_user)
文章目录
  1. 1. 重载(Overload)和重写(Override)
    1. 1.1. 重载(Overload)
      1. 1.1.1. 解释
      2. 1.1.2. 举例
    2. 1.2. 重写(Override)
  2. 2. 多态
  3. 3. C# 设计模式
  4. 4. 结构型模式
    1. 4.1. 适配器模式
      1. 4.1.1. 内容
      2. 4.1.2. 角色:
      3. 4.1.3. 适用场景
      4. 4.1.4. 实现
      5. 4.1.5. 扩展
    2. 4.2. 桥模式
      1. 4.2.1. 内容
      2. 4.2.2. 角色
      3. 4.2.3. 实现
      4. 4.2.4. 应用场景
      5. 4.2.5. 优点
    3. 4.3. 组合模式
      1. 4.3.1. 内容
      2. 4.3.2. 角色
      3. 4.3.3. 实现
      4. 4.3.4. 适用场景
      5. 4.3.5. 优点
    4. 4.4. 装饰模式
      1. 4.4.1. 内容
    5. 4.5. 外观模式
      1. 4.5.1. 内容
      2. 4.5.2. 角色
      3. 4.5.3. 实现
      4. 4.5.4. 使用场景
      5. 4.5.5. 优点
    6. 4.6. 享元模式(Flyweight)
      1. 4.6.1. 内容
      2. 4.6.2. 角色:
      3. 4.6.3. 适用场景
      4. 4.6.4. 实现
      5. 4.6.5. 优点
      6. 4.6.6. 缺点
    7. 4.7. 代理模式
      1. 4.7.1. 内容
      2. 4.7.2. 角色:
      3. 4.7.3. 适用场景
      4. 4.7.4. 实现