参考

https://refactoringguru.cn/

创建型

工厂模式

UML类图

  1. 产品 (Product) 将会对接口进行声明。 对于所有由创建者及其子类构建的对象, 这些接口都是通用的。

  2. 具体产品 (Concrete Products) 是产品接口的不同实现。

  3. 创建者 (Creator) 类声明返回产品对象的工厂方法。 该方法的返回对象类型必须与产品接口相匹配。

    你可以将工厂方法声明为抽象方法, 强制要求每个子类以不同方式实现该方法。 或者, 你也可以在基础工厂方法中返回默认产品类型。

    注意, 尽管它的名字是创建者, 但它最主要的职责并不是创建产品。 一般来说, 创建者类包含一些与产品相关的核心业务逻辑。 工厂方法将这些逻辑处理从具体产品类中分离出来。 打个比方, 大型软件开发公司拥有程序员培训部门。 但是, 这些公司的主要工作还是编写代码, 而非生产程序员。

  4. 具体创建者 (Concrete Creators) 将会重写基础工厂方法, 使其返回不同类型的产品。

    注意, 并不一定每次调用工厂方法都会创建新的实例。 工厂方法也可以返回缓存、 对象池或其他来源的已有对象。

Code

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
	interface IProduct
{
string DoStuff();
}

abstract class Creator
{
public abstract IProduct Create();

public virtual string SomeOperation()
{
return "Creator: The same creator's code has just worked with " + Create().DoStuff();
}
}

class ConcreteCreatorA : Creator
{
public override IProduct Create()
{
return new ConcreteProductA();
}
}

class ConcreteCreatorB : Creator
{
public override IProduct Create()
{
return new ConcreteProductB();
}
}


class ConcreteProductA : IProduct
{
public string DoStuff()
{
return "ConcreteProductA";
}
}

class ConcreteProductB : IProduct
{
public string DoStuff()
{
return "ConcreteProductB";
}
}

//IProduct product = new ConcreteCreatorA().Create();
//Console.WriteLine(product.DoStuff());

总结

工厂依赖产品接口,生产产品,具体工厂与具体产品分别继承工厂和实现产品接口

抽象工厂模式

UML类图

  1. 抽象产品 (Abstract Product) 为构成系列产品的一组不同但相关的产品声明接口。
  2. 具体产品 (Concrete Product) 是抽象产品的多种不同类型实现。 所有变体 (维多利亚/现代) 都必须实现相应的抽象产品 (椅子/沙发)。
  3. 抽象工厂 (Abstract Factory) 接口声明了一组创建各种抽象产品的方法。
  4. 具体工厂 (Concrete Factory) 实现抽象工厂的构建方法。 每个具体工厂都对应特定产品变体, 且仅创建此种产品变体。
  5. 尽管具体工厂会对具体产品进行初始化, 其构建方法签名必须返回相应的抽象产品。 这样, 使用工厂类的客户端代码就不会与工厂创建的特定产品变体耦合。 客户端 (Client) 只需通过抽象接口调用工厂和产品对象, 就能与任何具体工厂/产品变体交互。

Code

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
// 抽象工厂接口声明了一组能返回不同抽象产品的方法。这些产品属于同一个系列
// 且在高层主题或概念上具有相关性。同系列的产品通常能相互搭配使用。系列产
// 品可有多个变体,但不同变体的产品不能搭配使用。
interface AbstractFactory
{
AbstractProductA CreateProductA();
AbstractProductB CreateProductB();
}

// 具体工厂可生成属于同一变体的系列产品。工厂会确保其创建的产品能相互搭配
// 使用。具体工厂方法签名会返回一个抽象产品,但在方法内部则会对具体产品进
// 行实例化
// 每个具体工厂中都会包含一个相应的产品变体。
class ConcreteFactory1 : AbstractFactory
{
public AbstractProductA CreateProductA()
{
return new ProductA1();
}

public AbstractProductB CreateProductB()
{
return new ProductB1();
}
}

class ConcreteFactory2 : AbstractFactory
{
public AbstractProductA CreateProductA()
{
return new ProductA2();
}

public AbstractProductB CreateProductB()
{
return new ProductB2();
}
}

// 系列产品中的特定产品必须有一个基础接口。所有产品变体都必须实现这个接口
interface AbstractProductA
{
}

class ProductA1 : AbstractProductA
{
}

class ProductA2 : AbstractProductA
{

}

// 系列产品中的特定产品必须有一个基础接口。所有产品变体都必须实现这个接口
interface AbstractProductB
{
}

class ProductB1 : AbstractProductB
{
}

class ProductB2 : AbstractProductB
{
}

//AbstractProductA abstractProductA = new ConcreteFactory1().CreateProductA();
//AbstractProductB abstractProductB = new ConcreteFactory1().CreateProductB();

总结

抽象工厂生产抽象产品,具体工厂继承抽象工厂,具体产品继承抽象产品

生成器模式

UML类图

生成器设计模式结构

  1. 生成器 (Builder) 接口声明在所有类型生成器中通用的产品构造步骤。
  2. 具体生成器 (Concrete Builders) 提供构造过程的不同实现。 具体生成器也可以构造不遵循通用接口的产品。
  3. 产品 (Products) 是最终生成的对象。 由不同生成器构造的产品无需属于同一类层次结构或接口。
  4. 主管 (Director) 类定义调用构造步骤的顺序, 这样你就可以创建和复用特定的产品配置。
  5. 客户端 (Client) 必须将某个生成器对象与主管类关联。 一般情况下, 你只需通过主管类构造函数的参数进行一次性关联即可。 此后主管类就能使用生成器对象完成后续所有的构造任务。 但在客户端将生成器对象传递给主管类制造方法时还有另一种方式。 在这种情况下, 你在使用主管类生产产品时每次都可以使用不同的生成器。

Code

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
	interface IBuilder
{
void reset();
void step1();
void step2();
void step3();
}


class ConcreteA
{

}

class ConcreteABuilder: IBuilder
{
private ConcreteA product;

public ConcreteABuilder()
{
this.product = new ConcreteA();
}

public ConcreteA getResult()
{
return this.product;
}

public void reset()
{
this.product = new ConcreteA();
}

public void step1()
{
System.Console.WriteLine("ConcreteABuilder step1");
}

public void step2()
{
System.Console.WriteLine("ConcreteABuilder step2");
}

public void step3()
{
System.Console.WriteLine("ConcreteABuilder step3");
}
}

class ConcreteB
{

}

class ConcreteBBuilder: IBuilder
{
private ConcreteB product;

public ConcreteBBuilder()
{
this.product = new ConcreteB();
}

public ConcreteB getResult()
{
return this.product;
}

public void reset()
{
this.product = new ConcreteB();
}

public void step1()
{
System.Console.WriteLine("ConcreteBBuilder step1");
}

public void step2()
{
System.Console.WriteLine("ConcreteBBuilder step2");
}

public void step3()
{
System.Console.WriteLine("ConcreteBBuilder step3");
}
}

class Director
{
// 主管可同由客户端代码传递给自身的任何生成器实例进行交互。客户端可通
// 过这种方式改变最新组装完毕的产品的最终类型。主管可使用同样的生成步
// 骤创建多个产品变体
public void ConstructA(IBuilder abuilder)
{
abuilder.reset();
abuilder.step1();
abuilder.step2();
abuilder.step3();
}

public void ConstructB(IBuilder bbuilder)
{
bbuilder.reset();
bbuilder.step1();
bbuilder.step2();
bbuilder.step3();
}
}
//Director director = new Director();
//ConcreteABuilder abuilder = new ConcreteABuilder();
//ConcreteBBuilder bbuilder = new ConcreteBBuilder();
//director.ConstructA(abuilder);
//ConcreteA concreteA = abuilder.getResult();
//director.ConstructB(bbuilder);
//ConcreteB concreteB = bbuilder.getResult();

总结

具体Builder继承接口Builder,然后由Director使用

原型模式

UML类图

  1. 原型 (Prototype) 接口将对克隆方法进行声明。 在绝大多数情况下, 其中只会有一个名为 clone克隆的方法。
  2. 具体原型 (Concrete Prototype) 类将实现克隆方法。 除了将原始对象的数据复制到克隆体中之外, 该方法有时还需处理克隆过程中的极端情况, 例如克隆关联对象和梳理递归依赖等等。
  3. 客户端 (Client) 可以复制实现了原型接口的任何对象。

Code

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
// 基础原型
interface IPrototype
{
void SomeMethod();

IPrototype Clone();
}

// 具体原型。克隆方法会创建一个新对象并将其传递给构造函数。直到构造函数运
// 行完成前,它都拥有指向新克隆对象的引用。因此,任何人都无法访问未完全生
// 成的克隆对象。这可以保持克隆结果的一致
class ConcreteInstance : IPrototype
{
public void SomeMethod()
{
// Do something
}

public IPrototype Clone()
{
return new ConcreteInstance();
}
}

class PrototypeManager
{
private List<IPrototype> prototypes = new();

public void AddPrototype(IPrototype prototype)
{
prototypes.Add(prototype);
}

public IPrototype GetPrototype(int index)
{
return prototypes[index].Clone();
}
}

总结

具体原型继承原型接口,再由其他一个类聚合

单例模式

UML类图

  1. 单例 (Singleton) 类声明了一个名为 get­Instance获取实例的静态方法来返回其所属类的一个相同实例。

    单例的构造函数必须对客户端 (Client) 代码隐藏。 调用 获取实例方法必须是获取单例对象的唯一方式。

Code

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
class Singleton
{
private static Singleton instance;
private static readonly object lockObject = new object();

private Singleton() { }

public static Singleton Instance
{
get
{
if (instance == null)
{
lock (lockObject)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}

}
}

总结

单例需要注意多线程问题,屏蔽拷贝和构造,留出静态获取实例

结构型

适配器

UML类图

  1. 客户端 (Client) 是包含当前程序业务逻辑的类。
  2. 客户端接口 (Client Interface) 描述了其他类与客户端代码合作时必须遵循的协议。
  3. 服务 (Service) 中有一些功能类 (通常来自第三方或遗留系统)。 客户端与其接口不兼容, 因此无法直接调用其功能。
  4. 适配器 (Adapter) 是一个可以同时与客户端和服务交互的类: 它在实现客户端接口的同时封装了服务对象。 适配器接受客户端通过适配器接口发起的调用, 并将其转换为适用于被封装服务对象的调用。
  5. 客户端代码只需通过接口与适配器交互即可, 无需与具体的适配器类耦合。 因此, 你可以向程序中添加新类型的适配器而无需修改已有代码。 这在服务类的接口被更改或替换时很有用: 你无需修改客户端代码就可以创建新的适配器类。

Code

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
	// Target 定义客户端代码使用的特定于域的接口。
public interface ITarget
{
string GetRequest();
}

// Adaptee 包含一些有用的行为,但它的接口是与现有客户端代码不兼容。
// 被适应者需要一些在客户端代码可以使用它之前进行调整
class Adaptee
{
public string GetSpecificRequest()
{
return "Specific request.";
}
}

// 适配器使被适应者的界面与目标的接口兼容接口。
class Adapter : ITarget
{
private readonly Adaptee _adaptee;

public Adapter(Adaptee adaptee)
{
this._adaptee = adaptee;
}

public string GetRequest()
{
return $"This is '{this._adaptee.GetSpecificRequest()}'";
}
}

daptee adaptee = new Adaptee();
ITarget target = new Adapter(adaptee);
Console.WriteLine(target.GetRequest());

总结

桥接

UML类图

  1. 抽象部分 (Abstraction) 提供高层控制逻辑, 依赖于完成底层实际工作的实现对象。

  2. 实现部分 (Implementation) 为所有具体实现声明通用接口。 抽象部分仅能通过在这里声明的方法与实现对象交互。

    抽象部分可以列出和实现部分一样的方法, 但是抽象部分通常声明一些复杂行为, 这些行为依赖于多种由实现部分声明的原语操作。

  3. 具体实现 (Concrete Implementations) 中包括特定于平台的代码。

  4. 精确抽象 (Refined Abstraction) 提供控制逻辑的变体。 与其父类一样, 它们通过通用实现接口与不同的实现进行交互。

  5. 通常情况下, 客户端 (Client) 仅关心如何与抽象部分合作。 但是, 客户端需要将抽象对象与一个实现对象连接起来。

Code

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
	class Abstraction
{
protected IImplementation _implementation;

public Abstraction(IImplementation implementation)
{
_implementation = implementation;
}

public virtual void Operation1()
{
_implementation.Method1();
}

public virtual void Operation2()
{
_implementation.Method1();
_implementation.Method2();
}
}

class ExtendedAbstraction : Abstraction
{
public ExtendedAbstraction(IImplementation implementation) : base(implementation)
{
}

public override void Operation1()
{
_implementation.Method2();
}
}

interface IImplementation
{
void Method1();

void Method2();
}

class ConcreteImplementationA : IImplementation
{
public void Method1()
{
Console.WriteLine("A Method1");
}

public void Method2()
{
Console.WriteLine("A Method2");
}
}

class ConcreteImplementationB : IImplementation
{
public void Method1()
{
Console.WriteLine("B Method1");
}

public void Method2()
{
Console.WriteLine("B Method2 ");
}
}
Abstraction abstraction;
abstraction = new Abstraction(new ConcreteImplementationA());
abstraction.Operation2();

abstraction = new ExtendedAbstraction(new ConcreteImplementationB());
abstraction.Operation2();

总结

组合

UML类图

  1. 组件 (Component) 接口描述了树中简单项目和复杂项目所共有的操作。

  2. 叶节点 (Leaf) 是树的基本结构, 它不包含子项目。

    一般情况下, 叶节点最终会完成大部分的实际工作, 因为它们无法将工作指派给其他部分。

  3. 容器 (Container)——又名 “组合 (Composite)”——是包含叶节点或其他容器等子项目的单位。 容器不知道其子项目所属的具体类, 它只通过通用的组件接口与其子项目交互。

    容器接收到请求后会将工作分配给自己的子项目, 处理中间结果, 然后将最终结果返回给客户端。

  4. 客户端 (Client) 通过组件接口与所有项目交互。 因此, 客户端能以相同方式与树状结构中的简单或复杂项目交互。

Code

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
interface Component
{
void execute();
}

class Leaf : Component
{
private readonly int num = 0;

public Leaf(int n)
{
num = n;
}

public void execute()
{
System.Console.WriteLine("Leaf");
}
}

class Composite : Component
{
private List<Component> children = new List<Component>();

public void add(Component component)
{
children.Add(component);
}

public void remove(Component component)
{
children.Remove(component);
}

public void execute()
{
System.Console.WriteLine("Composite");
foreach (Component component in children)
{
component.execute();
}
}
}

总结

装饰

UML类图

  1. 部件 (Component) 声明封装器和被封装对象的公用接口。
  2. 具体部件 (Concrete Component) 类是被封装对象所属的类。 它定义了基础行为, 但装饰类可以改变这些行为。
  3. 基础装饰 (Base Decorator) 类拥有一个指向被封装对象的引用成员变量。 该变量的类型应当被声明为通用部件接口, 这样它就可以引用具体的部件和装饰。 装饰基类会将所有操作委派给被封装的对象。
  4. 具体装饰类 (Concrete Decorators) 定义了可动态添加到部件的额外行为。 具体装饰类会重写装饰基类的方法, 并在调用父类方法之前或之后进行额外的行为。
  5. 客户端 (Client) 可以使用多层装饰来封装部件, 只要它能使用通用接口与所有对象互动即可。

Code

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
	interface IComponent
{
void execute();
}

class ConcreteComponent : IComponent
{
public virtual void execute()
{
System.Console.WriteLine("ConcreteComponent");
}
}

class BaseDecorator : IComponent
{
protected IComponent component;

public BaseDecorator(IComponent component)
{
this.component = component;
}

public void SetComponent(IComponent component)
{
this.component = component;
}

public virtual void execute()
{
component.execute();
}
}

class ConcreteDecoratorA : BaseDecorator
{
public ConcreteDecoratorA(IComponent component) : base(component)
{
}

public override void execute()
{
base.execute();
System.Console.WriteLine("ConcreteDecoratorA");
}
}


var simple = new ConcreteComponent();
simple.execute();
ConcreteDecoratorA decorator1 = new ConcreteDecoratorA(simple);
decorator1.execute();

//ConcreteComponent
//Decorating...
//ConcreteComponent
//ConcreteDecoratorA
//ConcreteDecoratorB

外观

UML类图

  1. 外观 (Facade) 提供了一种访问特定子系统功能的便捷方式, 其了解如何重定向客户端请求, 知晓如何操作一切活动部件。

  2. 创建附加外观 (Additional Facade) 类可以避免多种不相关的功能污染单一外观, 使其变成又一个复杂结构。 客户端和其他外观都可使用附加外观。

  3. 复杂子系统 (Complex Subsystem) 由数十个不同对象构成。 如果要用这些对象完成有意义的工作, 你必须深入了解子系统的实现细节, 比如按照正确顺序初始化对象和为其提供正确格式的数据。

    子系统类不会意识到外观的存在, 它们在系统内运作并且相互之间可直接进行交互。

  4. 客户端 (Client) 使用外观代替对子系统对象的直接调用。

Code

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
class Subsystem1
{
public void Operation1()
{
System.Console.WriteLine("Subsystem1: Ready!");
}

public void OperationN()
{
System.Console.WriteLine("Subsystem1: Go!");
}
}

class Subsystem2
{
public void Operation1()
{
System.Console.WriteLine("Subsystem2: Get ready!");
}

public void OperationZ()
{
System.Console.WriteLine("Subsystem2: Fire!");
}
}

class Facade
{
private Subsystem1 _subsystem1;
private Subsystem2 _subsystem2;

public Facade(Subsystem1 subsystem1, Subsystem2 subsystem2)
{
this._subsystem1 = subsystem1;
this._subsystem2 = subsystem2;
}

public void Operation()
{
System.Console.WriteLine("Facade initializes subsystems:");
_subsystem1.Operation1();
_subsystem2.Operation1();
System.Console.WriteLine("Facade orders subsystems to perform the action:");
_subsystem1.OperationN();
_subsystem2.OperationZ();
}
}

Facade facade = new Facade(new Subsystem1(), new Subsystem2());
facade.Operation();

享元模式

对象的常量数据通常被称为内在状态, 其位于对象中, 其他对象只能读取但不能修改其数值。 而对象的其他状态常常能被其他对象 “从外部” 改变, 因此被称为外在状态。享元模式建议不在对象中存储外在状态, 而是将其传递给依赖于它的一个特殊方法。 程序只在对象中保存内在状态, 以方便在不同情景下重用。

UML

  1. 享元模式只是一种优化。 在应用该模式之前, 你要确定程序中存在与大量类似对象同时占用内存相关的内存消耗问题, 并且确保该问题无法使用其他更好的方式来解决。
  2. 享元 (Flyweight) 类包含原始对象中部分能在多个对象中共享的状态。 同一享元对象可在许多不同情景中使用。 享元中存储的状态被称为 “内在状态”。 传递给享元方法的状态被称为 “外在状态”。
  3. 情景 (Context) 类包含原始对象中各不相同的外在状态。 情景与享元对象组合在一起就能表示原始对象的全部状态。
  4. 通常情况下, 原始对象的行为会保留在享元类中。 因此调用享元方法必须提供部分外在状态作为参数。 但你也可将行为移动到情景类中, 然后将连入的享元作为单纯的数据对象。
  5. 客户端 (Client) 负责计算或存储享元的外在状态。 在客户端看来, 享元是一种可在运行时进行配置的模板对象, 具体的配置方式为向其方法中传入一些情景数据参数。
  6. 享元工厂 (Flyweight Factory) 会对已有享元的缓存池进行管理。 有了工厂后, 客户端就无需直接创建享元, 它们只需调用工厂并向其传递目标享元的一些内在状态即可。 工厂会根据参数在之前已创建的享元中进行查找, 如果找到满足条件的享元就将其返回; 如果没有找到就根据参数新建享元。

Code

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
class FlyweightFactory
{
private readonly Dictionary<string, Flyweight> flyweights = new Dictionary<string, Flyweight>();

public Flyweight GetFlyweight(string key)
{
if (!flyweights.ContainsKey(key))
{
flyweights[key] = new Flyweight();
}

return flyweights[key];
}
}

class Flyweight
{
public virtual void Operation()
{
Console.WriteLine("Flyweight Operation");
}
}

class Context
{
public Flyweight repeatingState { get; set; }
public string uniqueState { get; set; }

public Context(Flyweight repeatingState,string uniqueState)
{
this.repeatingState = repeatingState;
this.uniqueState = uniqueState;
}

public void Operation()
{
Console.WriteLine("Some Operations");
}
}


FlyweightFactory factory = new FlyweightFactory();
Flyweight flyweight = factory.GetFlyweight("key");
Context context = new Context(flyweight, "uniqueState");
context.Operation();

代理

UML类图

  1. 服务接口 (Service Interface) 声明了服务接口。 代理必须遵循该接口才能伪装成服务对象。

  2. 服务 (Service) 类提供了一些实用的业务逻辑。

  3. 代理 (Proxy) 类包含一个指向服务对象的引用成员变量。 代理完成其任务 (例如延迟初始化、 记录日志、 访问控制和缓存等) 后会将请求传递给服务对象。

    通常情况下, 代理会对其服务对象的整个生命周期进行管理。

  4. 客户端 (Client) 能通过同一接口与服务或代理进行交互, 所以你可在一切需要服务对象的代码中使用代理。

Code

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

interface ServiceInterface
{
void Operationn();
}

class Service : ServiceInterface
{
public void Operationn()
{
// Do something
}
}

class ServiceProxy : ServiceInterface
{
private ServiceInterface _service;

public ServiceProxy(ServiceInterface service)
{
_service = service;
}

public void Operationn()
{
// Do something before
_service.Operationn();
// Do something after
}
}

Service service = new Service();
ServiceProxy serviceProxy = new ServiceProxy(service);
serviceProxy.Operationn();

行为

责任链

UML类图

  1. 处理者 (Handler) 声明了所有具体处理者的通用接口。 该接口通常仅包含单个方法用于请求处理, 但有时其还会包含一个设置链上下个处理者的方法。

  2. 基础处理者 (Base Handler) 是一个可选的类, 你可以将所有处理者共用的样本代码放置在其中。

    通常情况下, 该类中定义了一个保存对于下个处理者引用的成员变量。 客户端可通过将处理者传递给上个处理者的构造函数或设定方法来创建链。 该类还可以实现默认的处理行为: 确定下个处理者存在后再将请求传递给它。

  3. 具体处理者 (Concrete Handlers) 包含处理请求的实际代码。 每个处理者接收到请求后, 都必须决定是否进行处理, 以及是否沿着链传递请求。

    处理者通常是独立且不可变的, 需要通过构造函数一次性地获得所有必要地数据。

  4. 客户端 (Client) 可根据程序逻辑一次性或者动态地生成链。 值得注意的是, 请求可发送给链上的任意一个处理者, 而非必须是第一个处理者。

Code
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
102
103
104
105
106
107
108
109
110
111
112
interface IHandler
{
IHandler SetNext(IHandler handler);


object? Handle(object request);
}

class AbstractHandler : IHandler
{
private IHandler? _nextHandler;

public IHandler SetNext(IHandler handler)
{
this._nextHandler = handler;
return handler;
}

public virtual object? Handle(object request)
{
if (this._nextHandler != null)
{
return this._nextHandler.Handle(request);
}
else
{
return null;
}
}
}


class AHandler: AbstractHandler
{
public override object? Handle(object request)
{
if((request as string) == "A")
{
return "A processes Request";
}
else
{
return base.Handle(request);
}
}
}

class BHandler : AbstractHandler
{
public override object? Handle(object request)
{
if ((request as string) == "B")
{
return "B processes Request";
}
else
{
return base.Handle(request);
}
}
}

class CHandler : AbstractHandler
{
public override object? Handle(object request)
{
if ((request as string) == "C")
{
return "C processes Request";
}
else
{
return base.Handle(request);
}
}
}

class EHandler : AbstractHandler
{
public override object? Handle(object request)
{
if ((request as string) == "E")
{
return "E processes Request";
}
else
{
return base.Handle(request);
}
}
}

var aH = new AHandler();
var bH = new BHandler();
var cH = new CHandler();
var eH = new EHandler();
aH.SetNext(bH).SetNext(cH).SetNext(eH);

AbstractHandler abstractHandler = aH;
foreach (var t in new List<string> { "A", "B", "C" ,"D","E"})
{
Console.WriteLine($"\nClient: Who wants a {t}?");
var result = abstractHandler.Handle(t);
if (result != null)
{
Console.Write($" {result}");
}
else
{
Console.WriteLine($" {t} was left untouched.");
}
}

命令

UML类图

  1. 发送者 (Sender)——亦称 “触发者 (Invoker)”——类负责对请求进行初始化, 其中必须包含一个成员变量来存储对于命令对象的引用。 发送者触发命令, 而不向接收者直接发送请求。 注意, 发送者并不负责创建命令对象: 它通常会通过构造函数从客户端处获得预先生成的命令。

  2. 命令 (Command) 接口通常仅声明一个执行命令的方法。

  3. 具体命令 (Concrete Commands) 会实现各种类型的请求。 具体命令自身并不完成工作, 而是会将调用委派给一个业务逻辑对象。 但为了简化代码, 这些类可以进行合并。

    接收对象执行方法所需的参数可以声明为具体命令的成员变量。 你可以将命令对象设为不可变, 仅允许通过构造函数对这些成员变量进行初始化。

  4. 接收者 (Receiver) 类包含部分业务逻辑。 几乎任何对象都可以作为接收者。 绝大部分命令只处理如何将请求传递到接收者的细节, 接收者自己会完成实际的工作。

  5. 客户端 (Client) 会创建并配置具体命令对象。 客户端必须将包括接收者实体在内的所有请求参数传递给命令的构造函数。 此后, 生成的命令就可以与一个或多个发送者相关联了

Code
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
interface ICommand
{
void Execute();
}

class ConcreteCommand_A : ICommand
{
public void Execute() {
Console.WriteLine("Do command A");
}
}

class Receiver_4_B
{
public void DoSomething_1(string s)
{
Console.WriteLine(s);
}

public void DoSomething_2(string s)
{
Console.WriteLine(s);
}
}

class ConcreteCommand_B: ICommand
{
private Receiver_4_B receiver;
private string para1 = string.Empty;
private string para2 = string.Empty;

public ConcreteCommand_B(Receiver_4_B receiver, string para1,string para2)
{
this.receiver = receiver;
this.para1 = para1;
this.para2 = para2;
}

public void Execute()
{
Console.WriteLine($"Do command B");
receiver.DoSomething_1(this.para1);
receiver.DoSomething_2(this.para2);
}
}

class Invoker
{
private ICommand? cmd_1;
private ICommand? cmd_2;

public void SetCmd1 (ICommand cmd)
{
this.cmd_1 = cmd;
}

public void SetCmd2(ICommand cmd)
{
this.cmd_2 = cmd;
}

public void Dosomething()
{
if (cmd_1 is ICommand)
{
cmd_1.Execute();
}
if(cmd_2 is ICommand)
{
cmd_2.Execute();
}
}
}


Invoker invoker = new Invoker();
invoker.SetCmd1(new ConcreteCommand_A());
Receiver_4_B receiver = new Receiver_4_B();
invoker.SetCmd2(new ConcreteCommand_B(receiver, "para1", "para2"));
invoker.Dosomething();

迭代器

UML类图

  1. 迭代器 (Iterator) 接口声明了遍历集合所需的操作: 获取下一个元素、 获取当前位置和重新开始迭代等。

  2. 具体迭代器 (Concrete Iterators) 实现遍历集合的一种特定算法。 迭代器对象必须跟踪自身遍历的进度。 这使得多个迭代器可以相互独立地遍历同一集合。

  3. 集合 (Collection) 接口声明一个或多个方法来获取与集合兼容的迭代器。 请注意, 返回方法的类型必须被声明为迭代器接口, 因此具体集合可以返回各种不同种类的迭代器。

  4. 具体集合 (Concrete Collections) 会在客户端请求迭代器时返回一个特定的具体迭代器类实体。 你可能会琢磨, 剩下的集合代码在什么地方呢? 不用担心, 它也会在同一个类中。 只是这些细节对于实际模式来说并不重要, 所以我们将其省略了而已。

  5. 客户端 (Client) 通过集合和迭代器的接口与两者进行交互。 这样一来客户端无需与具体类进行耦合, 允许同一客户端代码使用各种不同的集合和迭代器。

    客户端通常不会自行创建迭代器, 而是会从集合中获取。 但在特定情况下, 客户端可以直接创建一个迭代器 (例如当客户端需要自定义特殊迭代器时)。

Code

(暂略)

中介模式

UML类图

  1. 组件 (Component) 是各种包含业务逻辑的类。 每个组件都有一个指向中介者的引用, 该引用被声明为中介者接口类型。 组件不知道中介者实际所属的类, 因此你可通过将其连接到不同的中介者以使其能在其他程序中复用。

  2. 中介者 (Mediator) 接口声明了与组件交流的方法, 但通常仅包括一个通知方法。 组件可将任意上下文 (包括自己的对象) 作为该方法的参数, 只有这样接收组件和发送者类之间才不会耦合。

  3. 具体中介者 (Concrete Mediator) 封装了多种组件间的关系。 具体中介者通常会保存所有组件的引用并对其进行管理, 甚至有时会对其生命周期进行管理。

  4. 组件并不知道其他组件的情况。 如果组件内发生了重要事件, 它只能通知中介者。 中介者收到通知后能轻易地确定发送者, 这或许已足以判断接下来需要触发的组件了。

    对于组件来说, 中介者看上去完全就是一个黑箱。 发送者不知道最终会由谁来处理自己的请求, 接收者也不知道最初是谁发出了请求。

Code
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
interface IMediator
{
void Notify(object sender);
}

class BaseComponent
{
protected IMediator? mediator;

public BaseComponent(IMediator? mediator=null)
{
this.mediator = mediator;
}

public void SetMediator(IMediator? mediator)
{
this.mediator = mediator;
}

public virtual void Operation()
{
Console.WriteLine("Base Operation");
}
}

class ComponentA:BaseComponent
{
public override void Operation()
{
Console.WriteLine("Operation A");
this.mediator?.Notify(this);
}
}

class ComponentB : BaseComponent
{
public override void Operation()
{
Console.WriteLine("Operation B");
this.mediator?.Notify(this);
}
}
class ConcreteMediator : IMediator
{
private ComponentA _componentA;
private ComponentB _componentB;


public ConcreteMediator(ComponentA componentA, ComponentB componentB)
{
_componentA = componentA;
_componentA.SetMediator(this);
_componentB = componentB;
_componentB.SetMediator(this);
}

public void Notify(object sender)
{
if(sender is ComponentA)
{
ReactOnA();
}
if(sender is ComponentB)
{
ReactOnB();
}
}

public void ReactOnA()
{
Console.WriteLine("Mediator ReactOnA");
}

public void ReactOnB()
{
Console.WriteLine("Mediator ReactOnB");
}
}

ComponentA componentA = new ComponentA();
ComponentB componentB = new ComponentB();

ConcreteMediator mediator = new ConcreteMediator(componentA, componentB);
componentA.Operation();
componentB.Operation();

备忘录

UML类图

  1. 原发器 (Originator) 类可以生成自身状态的快照, 也可以在需要时通过快照恢复自身状态。

  2. 备忘录 (Memento) 是原发器状态快照的值对象 (value object)。 通常做法是将备忘录设为不可变的, 并通过构造函数一次性传递数据。

  3. 负责人 (Caretaker) 仅知道 “何时” 和 “为何” 捕捉原发器的状态, 以及何时恢复状态。

    负责人通过保存备忘录栈来记录原发器的历史状态。 当原发器需要回溯历史状态时, 负责人将从栈中获取最顶部的备忘录, 并将其传递给原发器的恢复 (restoration) 方法。

  4. 在该实现方法中, 备忘录类将被嵌套在原发器中。 这样原发器就可访问备忘录的成员变量和方法, 即使这些方法被声明为私有。 另一方面, 负责人对于备忘录的成员变量和方法的访问权限非常有限: 它们只能在栈中保存备忘录, 而不能修改其状态。

Code

(暂略)

观察者

UML类图

  1. 发布者 (Publisher) 会向其他对象发送值得关注的事件。 事件会在发布者自身状态改变或执行特定行为后发生。 发布者中包含一个允许新订阅者加入和当前订阅者离开列表的订阅构架。
  2. 当新事件发生时, 发送者会遍历订阅列表并调用每个订阅者对象的通知方法。 该方法是在订阅者接口中声明的。
  3. 订阅者 (Subscriber) 接口声明了通知接口。 在绝大多数情况下, 该接口仅包含一个 update更新方法。 该方法可以拥有多个参数, 使发布者能在更新时传递事件的详细信息。
  4. 具体订阅者 (Concrete Subscribers) 可以执行一些操作来回应发布者的通知。 所有具体订阅者类都实现了同样的接口, 因此发布者不需要与具体类相耦合。
  5. 订阅者通常需要一些上下文信息来正确地处理更新。 因此, 发布者通常会将一些上下文数据作为通知方法的参数进行传递。 发布者也可将自身作为参数进行传递, 使订阅者直接获取所需的数据。
  6. 客户端 (Client) 会分别创建发布者和订阅者对象, 然后为订阅者注册发布者更新。
Code
1

状态

UML类图

  1. 上下文 (Context) 保存了对于一个具体状态对象的引用, 并会将所有与该状态相关的工作委派给它。 上下文通过状态接口与状态对象交互, 且会提供一个设置器用于传递新的状态对象。

  2. 状态 (State) 接口会声明特定于状态的方法。 这些方法应能被其他所有具体状态所理解, 因为你不希望某些状态所拥有的方法永远不会被调用。

  3. 具体状态 (Concrete States) 会自行实现特定于状态的方法。 为了避免多个状态中包含相似代码, 你可以提供一个封装有部分通用行为的中间抽象类。

    状态对象可存储对于上下文对象的反向引用。 状态可以通过该引用从上下文处获取所需信息, 并且能触发状态转移。

  4. 上下文和具体状态都可以设置上下文的下个状态, 并可通过替换连接到上下文的状态对象来完成实际的状态转换。

Code
1

策略

UML类图

  1. 上下文 (Context) 维护指向具体策略的引用, 且仅通过策略接口与该对象进行交流。
  2. 策略 (Strategy) 接口是所有具体策略的通用接口, 它声明了一个上下文用于执行策略的方法。
  3. 具体策略 (Concrete Strategies) 实现了上下文所用算法的各种不同变体。
  4. 当上下文需要运行算法时, 它会在其已连接的策略对象上调用执行方法。 上下文不清楚其所涉及的策略类型与算法的执行方式。
  5. 客户端 (Client) 会创建一个特定策略对象并将其传递给上下文。 上下文则会提供一个设置器以便客户端在运行时替换相关联的策略。
Code
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
	public interface IStrategy
{
object DoAlgorithm(object data);
}

class Context
{
private IStrategy? _strategy;

public Context()
{

}

public Context(IStrategy strategy)
{
this._strategy = strategy;
}

public void SetStrategy(IStrategy strategy)
{
this._strategy = strategy;
}

public void DoSomeBusinessLogic()
{
var result = this._strategy?.DoAlgorithm(new List<string> { "a", "b", "c", "d", "e" });
if(result != null)
{
var l = result as List<string>;
foreach (var item in l)
{
Console.WriteLine(item);
}
}
}
}

class ConcreteStrategyA : IStrategy
{
public object DoAlgorithm(object data)
{
var list = data as List<string>;
list.Sort();

return list;
}
}

class ConcreteStrategyB : IStrategy
{
public object DoAlgorithm(object data)
{
var list = data as List<string>;
list.Sort();
list.Reverse();

return list;
}
}
var context = new Context();
Console.WriteLine("A");
context.SetStrategy(new ConcreteStrategyA());
context.DoSomeBusinessLogic();
Console.WriteLine("B");
context.SetStrategy(new ConcreteStrategyB());
context.DoSomeBusinessLogic();

模板方法

UML类图

  1. 抽象类 (Abstract­Class) 会声明作为算法步骤的方法, 以及依次调用它们的实际模板方法。 算法步骤可以被声明为 抽象类型, 也可以提供一些默认实现。
  2. 具体类 (Concrete­Class) 可以重写所有步骤, 但不能重写模板方法自身。
Code

就是简而言之的父类,父类事先定义好逻辑,然后被继承,派生类可以更改内部虚函数实现细节

访问者

UML类图

  1. 访问者 (Visitor) 接口声明了一系列以对象结构的具体元素为参数的访问者方法。 如果编程语言支持重载, 这些方法的名称可以是相同的, 但是其参数一定是不同的。
  2. 具体访问者 (Concrete Visitor) 会为不同的具体元素类实现相同行为的几个不同版本。
  3. 元素 (Element) 接口声明了一个方法来 “接收” 访问者。 该方法必须有一个参数被声明为访问者接口类型。
  4. 具体元素 (Concrete Element) 必须实现接收方法。 该方法的目的是根据当前元素类将其调用重定向到相应访问者的方法。 请注意, 即使元素基类实现了该方法, 所有子类都必须对其进行重写并调用访问者对象中的合适方法。
  5. 客户端 (Client) 通常会作为集合或其他复杂对象 (例如一个组合树) 的代表。 客户端通常不知晓所有的具体元素类, 因为它们会通过抽象接口与集合中的对象进行交互。
Code

(暂略)