设计模式

Last updated on 3 months ago

设计模式

1. 设计模式-代理模式

概念

代理模式(Proxy Pattern)。属于结构型模式。为其它对象提供一种代理以控制对该对象的访问。

代理的概念很简单,就是想调用A类方法时,不直接调用,而是通过调用代理B类的方法,由B类方法去调用A类的方法。

模式中的角色

  1. Subject(抽象对象):它声明了真实对象和代理对象的共同接口,这样一来在任何使用真实对象的地方都可以使用代理对象,客户端通常需要针对抽象对象角色进行编程。
  2. Proxy(代理对象):代理对象与真实对象实现相同的接口,所以它能够在任何时刻都能够代理真实对象。代理对象内部包含有对真实对象的引用,所以她可以操作真实对象,同时也可以附加其他的操作,相当于对真实对象进行封装。
  3. RealSubject(真实对象):它定义了所代表的真实对象,在真实对象角色中实现了真实的业务操作,客户端可以通过代理对象间接调用真实对象中定义的操作。

栗子

一个害羞男孩追求一个叫如花的女孩,但是自己不敢送礼物,就找了一个朋友代理他给如花送礼物

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
/// <summary>
/// 送礼物的人 (抽象主题角色)
/// </summary>
public abstract class GiveGiftPerson
{
//送礼物功能
public abstract void GiveGift();
}

/// <summary>
/// 害羞男孩 (真实主题角色)
/// </summary>
public class ShyBoy : GiveGiftPerson
{
public override void GiveGift()
{
Console.WriteLine("害羞男孩送礼物给如花~~");
}
}

/// <summary>
/// 害羞男孩的朋友(代理主题角色)
/// </summary>
public class Friend : GiveGiftPerson
{
//引用真实主题实例
ShyBoy shyboy = new ShyBoy();
public override void GiveGift()
{
//朋友一顿分析后,觉得如花是个好人
bool IsRuhuaGood = true;
if (IsRuhuaGood)
{
//调用真实主题的方法
shyboy.GiveGift();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
class Program
{
static void Main(string[] args)
{
//因为不能直接访问到真实对象所以我们不能直接new ShyBoy()去送礼物。

//代码中使用的是代理对象
Friend friend = new Friend();
friend.GiveGift();
Console.ReadKey();
}
}

优缺点

优点

  1. 代理模式能够将调用用于真正被调用的对象隔离,在一定程度上降低了系统的耦合度。
  2. 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。

缺点

  1. 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,例如保护代理。
  2. 实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂,例如远程代理。

使用场景

  1. 当客户端对象需要访问远程主机中的对象时可以使用远程代理
  2. 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理,例如一个对象需要很长时间才能完成加载时。
  3. 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可。
  4. 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。
  5. 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。

其他

  1. 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使(Ambassador)。
  2. 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
  3. 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
  4. 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
  5. 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等。

参考资料

C#设计模式笔记之代理模式 (Proxy Pattern) - 知乎 (zhihu.com)

C#设计模式——代理模式 - 心中的天空之城 - 博客园 (cnblogs.com)

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
/// <summary>
/// 电脑部件
/// </summary>
public class Product
{
/// <summary>
/// CPU
/// </summary>
public string CPUNAME { get; set; }

/// <summary>
/// 风扇
/// </summary>
public string FANNAME { get; set; }

/// <summary>
/// 内存
/// </summary>
public string MEMORY { get; set; }

/// <summary>
/// 硬盘
/// </summary>
public string HARDDISK { get; set; }
}

步骤

那现在抽象这些个方法,意思就是如果要安装一部电脑,需要操作这些个步骤。

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
/// <summary>
/// 构造者
/// </summary>
public abstract class Builder
{
/// <summary>
/// 安装CPU
/// </summary>
public abstract void BUILD_CPU();

/// <summary>
/// 安装风扇
/// </summary>
public abstract void BUILD_FAN();

/// <summary>
/// 安装内存条
/// </summary>
public abstract void BUILD_MEMORY();

/// <summary>
/// 安装硬盘
/// </summary>
public abstract void BUILD_DARDDISK();

/// <summary>
/// 返回安装的电脑
/// </summary>
/// <returns></returns>
public abstract Product GetComputer();
}

上面这个只是构建电脑的步骤,但是还具体不到实操,接着整

方案

现在准备两种构建电脑的方案

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
/// <summary>
/// 方案1:产品A
/// </summary>
public class ProductA : Builder
{
private readonly Product _computer = new Product();

public override void BUILD_CPU()
{
_computer.CPUNAME = "I5";
}

public override void BUILD_DARDDISK()
{
_computer.HARDDISK = "金士顿";
}

public override void BUILD_FAN()
{
_computer.FANNAME = "九州风神";
}

public override void BUILD_MEMORY()
{
_computer.MEMORY = "海盗船";
}

public override Product GetComputer()
{
return _computer;
}
}
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
/// <summary>
/// 方案2产品B
/// </summary>
public class ProductB : Builder
{
private readonly Product _computer = new Product();

public override void BUILD_CPU()
{
_computer.CPUNAME = "I7";
}

public override void BUILD_DARDDISK()
{
_computer.HARDDISK = "闪迪";
}

public override void BUILD_FAN()
{
_computer.FANNAME = "九州风神";
}

public override void BUILD_MEMORY()
{
_computer.MEMORY = "海盗船";
}

public override Product GetComputer()
{
return _computer;
}
}

ToDo

现在已知

  1. 构建电脑的部件:CPU、内存、风扇、硬盘
  2. 构建电脑的步骤:CPU、内存、风扇、硬盘、出厂
  3. 构建电脑的方案:各个部件具体的品牌

现在需要确定谁来干,那就老板来干吧,小本买卖,当然,如果人多,可以多整几个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// <summary>
/// 老板来构建电脑
/// </summary>
public class Bosss
{
//构建
public void Construct(Builder builder)
{
// 安装CPU
builder.BUILD_CPU();

//安装硬盘
builder.BUILD_DARDDISK();

//安装风扇
builder.BUILD_FAN();

//安装内存
builder.BUILD_MEMORY();

//完事-出厂
builder.GetComputer();
}
}

最后,演示一下整体流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Program
{
static void Main(string[] args)
{
//小明进店,发现老板不在店,叫来老板(实例化)
Bosss bosss = new Bosss();

//老板介绍有产品A 和 产品B,分别对应不同的配置。问小明要哪个?

//小明说小孩子才做选择题,成年人选择都要,然后老板然后去组装(生产)
bosss.Construct(new ProductA());
bosss.Construct(new ProductB());

//独立性,扩展性,需要扩展了就直接添加一个品种就行
Console.ReadLine();
}
}

3. 设计模式-桥接模式

一句话

一个类可以通过多角度来分类,每一种分类都可能变化,那么就把多角度分离出来让各个角度都能独立变化,降低各个角度间的耦合。

举个栗子

画图形,图形有图形种类和颜色两个指标组成,如果现在要画三种不同的图形并且会有三种不同的颜色,按照现有要求,会每个图形+颜色为一个类的话,会产生9个类或者方法来完成,如果要加图形或者颜色的话,类或者方法会增长的很快,所以需要桥接模式。

形状抽象

1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class Shape
{
//形状内部包含了另一个维度:color
protected IColor color;

public void SetColor(IColor color)
{
this.color = color;
}

//设置形状
public abstract void Draw();
}

颜色接口

1
2
3
4
5
6
7
/// <summary>
/// 颜色接口
/// </summary>
public interface IColor
{
void Paint(string shape);
}

颜色

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
/// <summary>
/// 蓝色
/// </summary>
public class Blue : IColor
{
public void Paint(string shape)
{
Console.WriteLine($"蓝色的{shape}");
}
}
/// <summary>
/// 黄色
/// </summary>
public class Yellow : IColor
{
public void Paint(string shape)
{
Console.WriteLine($"黄色的{shape}");
}
}
/// <summary>
/// 红色
/// </summary>
public class Red : IColor
{
public void Paint(string shape)
{
Console.WriteLine($"红色的{shape}");
}
}

形状

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
/// <summary>
/// 圆形
/// </summary>
public class Circle : Shape
{
public override void Draw()
{
color.Paint("圆形");
}
}
/// <summary>
/// 长方形
/// </summary>
public class Rectangle : Shape
{
public override void Draw()
{
color.Paint("长方形");
}
}
/// <summary>
/// 三角形
/// </summary>
public class Triangle : Shape
{
public override void Draw()
{
color.Paint("三角形");
}
}

创建形状

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void Main(string[] args)
{
Shape circle = new Circle();
IColor blue = new Blue();
circle.SetColor(blue);//设置颜色
circle.Draw();//画图

Shape triangle = new Triangle();
triangle.SetColor(blue);
triangle.Draw();

Console.ReadLine();
}

//输出:
//蓝色圆形
//蓝色三角形

4. 设计模式-适配器模式

一句话

有对象适配、有类适配。类似于插座转化器,如下栗子。

类适配模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#region 类适配模式

public interface ITwo
{
void Request1();
}

public abstract class ThreeAdaptee
{
public void Request2()
{
Console.WriteLine("我是三个孔的插头");
}
}

public class ThreeToTwoAdapter : ThreeAdaptee, ITwo
{
public void Request1()
{
this.Request2();
}
}

#endregion

类适配模式运行

1
2
3
4
5
ITwo change = new ThreeToTwoAdapter();

change.Request1();

Console.ReadLine();

对象适配器

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
#region 对象适配模式

public class Two//两口的插头
{
public virtual void Request1()
{
Console.WriteLine("2个孔的插头可以使用");
}
}

public class Three
{
public void Request2()
{
Console.WriteLine("3个孔的插头可以使用");
}
}

public class TwoToThreeAdapter : Two
{
Three three = new Three();
public override void Request1()
{
three.Request2();
}
}

#endregion

对象适配器运行

1
2
3
4
5
Two two = new TwoToThreeAdapter();

two.Request1();

Console.ReadLine();

优缺点

  1. 单一职责原则(优点)
  2. 开闭原则(优点)
  3. 代码复杂度增加(缺点)

适应场景

  1. 如果第三方的接口或者代码于现有的接口、代码不兼容的时候,可以添加一个中间适配器层来解决这样的问题。

5. 设计模式-外观模式

一句话

为子系统中的一组接口提供一个一致的界面,用来访问子系统中的一群接口

举个栗子

在家里看个电影需要干些啥。把投影仪、功放、屏幕、DVD、灯光 这五个看做五个子系统。

  1. 打开投影仪
  2. 打开功放
  3. 打开屏幕
  4. 打开DVD
  5. 打开灯光

看完了怎么关闭

  1. 关闭投影仪
  2. 关闭功放
  3. 关闭屏幕
  4. 关闭DVD
  5. 关闭灯光

具体方法

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
/// <summary> 
/// 投影仪
/// </summary>
public class Projector
{
public void OpenProjector()
{
Console.WriteLine("打开投影仪");
}
public void CloseProjector()
{
Console.WriteLine("关闭投影仪");
}
public void SetWideScreen()
{
Console.WriteLine("投影仪状态为宽屏模式");
}
public void SetStandardScreen()
{
Console.WriteLine("投影仪状态为标准模式");
}
}

/// <summary>
/// 功放机
/// </summary>
public class Amplifier
{
public void OpenAmplifier()
{
Console.WriteLine("打开功放机");
}
public void CloseAmplifier()
{
Console.WriteLine("关闭功放机");
}
}

/// <summary>
/// 屏幕
/// </summary>
public class Screen
{
public void OpenScreen()
{
Console.WriteLine("打开屏幕");
}
public void CloseScreen()
{
Console.WriteLine("关闭屏幕");
}
}

/// <summary>
/// DVD播放器
/// </summary>
public class DVDPlayer
{
public void OpenDVDPlayer()
{
Console.WriteLine("打开 DVD 播放器");
}
public void CloseDVDPlayer()
{
Console.WriteLine("关闭 DVD 播放器");
}
}

/// <summary>
/// 灯光
/// </summary>
public class Light
{
public void OpenLight()
{
Console.WriteLine("打开灯光");
}
public void CloseLight()
{
Console.WriteLine("关闭灯光");
}
}

定义一个外观

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
/// <summary> 
/// 定义一个外观
/// </summary>
public class MovieFacade
{
/// <summary>
/// 在外观类中必须保存有子系统中各个对象
/// </summary>
private Projector projector;
private Amplifier amplifier;
private Screen screen;
private DVDPlayer dvdPlayer;
private Light light;

public MovieFacade()
{
projector = new Projector();
amplifier = new Amplifier();
screen = new Screen();
dvdPlayer = new DVDPlayer();
light = new Light();
}

/// <summary>
/// 打开电影
/// </summary>
public void OpenMovie()
{
//先打开投影仪
projector.OpenProjector();
//再打开功放
amplifier.OpenAmplifier();
//再打开屏幕
screen.OpenScreen();
//再打开 DVD
dvdPlayer.OpenDVDPlayer();
//再打开灯光
light.OpenLight();
}

/// <summary>
/// 关闭电影
/// </summary>
public void CloseMovie()
{
//关闭投影仪
projector.CloseProjector();
//关闭功放
amplifier.CloseAmplifier();
//关闭屏幕
screen.CloseScreen();
//关闭 DVD
dvdPlayer.CloseDVDPlayer();
//关闭灯光
light.CloseLight();
}
}

看电影

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void Main(string[] args)
{
MovieFacade movie = new MovieFacade();
//Projector projector = new Projector();

//首先是观看电影
movie.OpenMovie();
Console.WriteLine();

//然后是将投影仪模式调到宽屏模式
//projector.SetWideScreen();
//再将投影仪模式调回普通模式
//projector.SetStandardScreen();
//Console.WriteLine();

//最后就是关闭电影了
movie.CloseMovie();
Console.ReadKey();
}

优缺点

优点

  1. 外观模式对客户端屏蔽了子系统组件,从而简化了接口,减少了客户处理的对象数目并使得子系统的使用更加简单。
  2. 外观模式实现了客户端和子系统之间的松耦合关系,而子系统内部的组件是紧耦合的。松耦合使得子系统的组件变化不会影响到客户端。

缺点

  1. 如果新增新的子系统可能需要修改外观类,这就不符合开闭原则,不过这也是没法避免的。

最后

外观模式也叫门面模式。属于结构型模式。

为子系统中的一系列接口提供一个一致的界面,该模式提供了一个高层接口,这个接口使得这一子系统更加容易使用。

外观模式是通过定义一个外观类将子类包装起来,来简化客户端所调用的方法.

6. 设计模式-享元模式

概念

FlyweightPattern

​ 在软件开发中我们经常遇到多次使用相似或者相同对象的情况,如果每次使用这个对象都去new一个新的实例会很浪费资源。这时候很多人会想到前边介绍过的一个设计模式:

​ 原型模式,原型模式通过拷贝现有对象来生成一个新的实例,使用拷贝来替代new。原型模式可以很好的解决创建多个相同/相似实例的问题,为什么还要用享元模式呢?

​ 这是因为这两种模式的使用场景是不同的,原型模式侧重于”创建“,我们通过拷贝确确实实的创建了新的实例,它属于创建型设计模式;而享元模式侧重于“重用”,即如果有现有的实例就不去创建了,直接拿来用就行了。

举个栗子

抽象车类

1
2
3
4
5
public abstract class Car
{
//开车
public abstract void Use(Driver d);
}

具体车类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class RealCar : Car
{
//颜色
public string Color { get; set; }
public RealCar(string color)
{
this.Color = color;
}
//开车
public override void Use(Driver d)
{
Console.WriteLine($"{d.Name}{this.Color}的车");
}
}

车库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// <summary>
/// 车库
/// </summary>
public class CarFactory
{
private Dictionary<string, Car> carPool = new Dictionary<string, Car>();
//初始的时候,只有红色和绿色两辆汽车
public CarFactory()
{
carPool.Add("红色", new RealCar("红色"));
carPool.Add("绿色", new RealCar("蓝色"));
}
//获取汽车
public Car GetCar(string key)
{
//如果车库有就用车库里的车,车库没有就买一个(new一个)
if (!carPool.ContainsKey(key))
{
carPool.Add(key, new RealCar(key));
Console.WriteLine("没有{0}的车,就买一个", key);
}
return carPool[key];
}
}

司机类

1
2
3
4
5
6
7
8
9
10
11
/// <summary>
/// 司机类
/// </summary>
public class Driver
{
public string Name { get; set; }
public Driver(string name)
{
this.Name = name;
}
}

实际应用

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
static void Main(string[] args)
{
CarFactory carFactory = new CarFactory();

//小头爸爸开蓝色的车
Driver d1 = new Driver("小头爸爸");
Car c1 = carFactory.GetCar("蓝色");
c1.Use(d1);

//扁头妈妈开蓝色的车
Driver d2 = new Driver("扁头妈妈");
Car c2 = carFactory.GetCar("蓝色");
c2.Use(d2);

if (c1.Equals(c2))
{
Console.WriteLine("小头爸爸和扁头妈妈开的是同一辆车");
}

//车库没有白色的车,就new一辆白色的车
Driver d3 = new Driver("大头儿子");
Car c3 = carFactory.GetCar("白色");
c3.Use(d3);
Console.ReadKey();
}

使用场景

​ 当系统中大量使用某些相同或者相似的对象,这些对象要耗费大量的内存,并且这些对象剔除外部状态后可以通过一个对象来替代,这时可以考虑使用享元模式。在软件系统中享元模式大量用于各种池技术,如数据库连接对象池,字符串缓存池,HttpApplication池等。

优缺点

优点

  1. 降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。

缺点

  1. 需要分离对象的外部状态和内部状态,使用不当会引起线程安全问题,提高了系统的复杂度。

7. 设计模式-原型模式

一句话

用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。

举个栗子

以手机来做个原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/// <summary>
/// 手机原型
/// </summary>
public abstract class MobilePhonePrototype
{
private string _brand;

/// <summary>
/// 品牌
/// </summary>
public string Brand
{
get { return _brand; }
}

public MobilePhonePrototype(string brand)
{
this._brand = brand;
}

public abstract MobilePhonePrototype Clone();
}

小米原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/// <summary>
/// 小米手机原型
/// </summary>
public class XiaoMiPrototype : MobilePhonePrototype
{
public XiaoMiPrototype(string brand)
: base(brand)
{
}

public override MobilePhonePrototype Clone()
{
return (MobilePhonePrototype)this.MemberwiseClone();
}
}

苹果手机原型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// <summary>
/// 苹果手机原型
/// </summary>
public class ApplePrototype : MobilePhonePrototype
{
public ApplePrototype(string brand)
: base(brand)
{
}
public override MobilePhonePrototype Clone()
{
return (MobilePhonePrototype)this.MemberwiseClone();
}
}

最后

由于原型模式Prototype是一种创建型设计模式,它关注的是大量相同或者相似的对象创建问题。通过new一个对象后,然后通过new出来的对象进行复制,创建出相同的对象。也就是通过这种方式来创建对象。

1
2
3
4
5
6
7
XiaoMiPrototype xiaomi = new XiaoMiPrototype("小米");
XiaoMiPrototype xiaomi2 = (XiaoMiPrototype)xiaomi.Clone();
Console.WriteLine(xiaomi2.Brand);

ApplePrototype iphone = new ApplePrototype("iPhone7 Plus");
ApplePrototype iphone2 = (ApplePrototype)iphone.Clone();
Console.WriteLine(iphone2.Brand);

8. 设计模式-装饰器模式

一句话

当我们需要动态添加类的功能同时不改变类的结构时可以使用装饰者模式,装饰类本质是一个现有类的包装。

举个栗子

创建一个人,然后给他穿衣服。

创建一个人

1
2
3
4
5
6
7
8
9
10
11
12
/// <summary>
/// 人类 具体组件角色
/// </summary>
public class Person : AbstractPerson
{
public string Name { get; set; }
//待添加功能的Show方法,具体组件中的Show方法只有原始功能
public override void Show()
{
Console.Write($"打扮的人是{this.Name}:");
}
}

抽象这个人

1
2
3
4
5
6
7
8
/// <summary>
/// 抽象人类
/// </summary>
public abstract class AbstractPerson
{
//展示装饰方法,我们使用装饰器模式的目的就是为了扩展这个接口的功能
public abstract void Show();
}

抽象穿衣服方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//因为我们装饰后的person要直接替换装饰前的person,所以必须继承AbstractPerson
public abstract class Finery : AbstractPerson
{
protected AbstractPerson person;
//设置要打扮的人
public void SetPerson(AbstractPerson p)
{
this.person = p;
}
public override void Show()
{
if (person != null)
{
person.Show();
}
}
}

衣服库

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
//t恤
public class TShirt : Finery
{
public override void Show()
{
base.Show();
Console.Write("大t恤 ");
}
}
//网鞋
public class Sneaker : Finery
{
public override void Show()
{
base.Show();
Console.Write("网鞋 ");
}

}
//西装
public class Suit : Finery
{
public override void Show()
{
base.Show();
Console.Write("西装 ");
}
}
//领带
public class Tie : Finery
{
public override void Show()
{
base.Show();
Console.Write("领带 ");
}
}
//皮鞋
public class Leather : Finery
{
public override void Show()
{
base.Show();
Console.Write("皮鞋 ");
}
}

穿衣服咯

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
static void Main(string[] args)
{
//首先要有打扮的人
AbstractPerson xc = new Person() { Name = "小菜" };

Console.WriteLine("第一种装饰-------------------");
Finery personWithsuit = new Suit();
Finery personWithtie = new Tie();
Finery personWithleather = new Leather();
//装饰过程
personWithsuit.SetPerson(xc);//给小菜穿上西服
personWithtie.SetPerson(personWithsuit);//给穿上西服的小菜带上领带
personWithleather.SetPerson(personWithtie);//给穿上西服带上领带的小菜穿上皮鞋
personWithleather.Show();

Console.WriteLine();
Console.WriteLine("第二种装饰-------------------");
Finery personWithTshirt = new TShirt();
Finery personWithSneaker = new Sneaker();
//装饰过程
personWithTshirt.SetPerson(xc);//给小菜穿上t恤
personWithSneaker.SetPerson(personWithTshirt);//给穿上t恤的小菜穿上网球鞋
personWithSneaker.Show();

Console.ReadKey();
}

输出

1
2
3
4
第一种装饰-------------------
打扮的人是小菜:西装 领带 皮鞋
第二种装饰-------------------
打扮的人是小菜:大t恤 网鞋

优缺点

优点

  1. 一个类需要添加一些功能,而这些功能按数目、顺序组合形成的效果不一样,如果用继承会造成子类过多,装饰者模式可以很好地解决这个问题;
  2. 使用装饰者模式我们可以动态的添加/删除类的功能,灵活性好

缺点

  1. 多层装饰比较复杂,我们需要注意装饰顺序等因素。如先穿内裤再穿裤子,是正常人;而先穿裤子再穿内裤就是超人了。在开发中先过滤字符串再加密,和先加密字符串再过滤的效果是完全不同的。

9. 设计模式-组合模式

一句话

在树形结构的处理中模糊了对象和对象组的概念,使用对象和对象组采用了统一的接口,让我们可以像处理简单对象一样处理对象组。

举个例子

首先得有个树结构,最先想到的就是OA系统里的组织结构。集团总公司、分公司、部门、岗位、人员等等,下面以员工和部门举栗子。

先抽个象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// <summary>
/// 抽象部件 定义了树枝和树叶的公共属性和接口
/// </summary>
public abstract class Component
{
public string name;

public Component(string name)
{
this.name = name;
}

//添加子节点
public abstract void Add(Component c);

//删除子节点
public abstract void Remove(Component c);

//展示方法,dept为节点深度
public abstract void Display(int dept);
}

树叶子:员工

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//具体员工,树形结构的Leaf
public class Employee : Component
{
public Employee(string name) : base(name)
{
this.name = name;
}
//Leaf不能添加/删除子节点所以空实现
public override void Add(Component c)
{
}
public override void Remove(Component c)
{

}
public override void Display(int dept)
{
Console.WriteLine(new string('-', dept) + name);
}
}

树枝:部门

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
/// <summary>
/// 部门类,相当于树枝
/// </summary>
public class Depart : Component
{
public Depart(string name) : base(name)
{
this.name = name;
}
//添加子节点
public List<Component> children = new List<Component>();
public override void Add(Component c)
{
children.Add(c);
}
//删除子节点
public override void Remove(Component c)
{
children.Remove(c);
}
//展示自己和和内部的所有子节点,这里是组合模式的核心
public override void Display(int dept)
{
Console.WriteLine(new string('-', dept) + name);

foreach (var item in children)
{
//这里用到了递归的思想
item.Display(dept + 4);
}
}
}

最后:攒

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
static void Main(string[] args)
{
Component DepartA = new Depart("A总公司");
Component DepartAX = new Depart("AX部门");
Component DepartAY = new Depart("AY部门");
Component DepartAX1 = new Depart("AX1子部门");
Component DepartAX2 = new Depart("AX2子部门");
Component Ae1 = new Employee("公司直属员工1");
Component AXe1 = new Employee("AX部门员工1");
Component AX1e1 = new Employee("AX1部门员工1");
Component AX1e2 = new Employee("AX1部门员工2");
Component AYe1 = new Employee("AY部门员工1");
Component AYe2 = new Employee("AY部门员工2");
DepartA.Add(Ae1);
DepartA.Add(DepartAX);
DepartA.Add(DepartAY);
DepartAX.Add(AXe1);
DepartAX.Add(DepartAX1);
DepartAX.Add(DepartAX2);
DepartAX1.Add(AX1e1);
DepartAX1.Add(AX1e2);
DepartAY.Add(AYe1);
DepartAY.Add(AYe2);
//遍历总公司
DepartA.Display(1);
Console.ReadKey();
}

image-20220505143244328


设计模式
http://example.com/2022/05/05/Csharp-设计模式/
Author
Harris
Posted on
May 5, 2022
Licensed under