0、参考
(1)https://www.toutiao.com/article/7137101970736824867
(2)https://www.toutiao.com/article/7250284810353213964
(3)很好:https://zhuanlan.zhihu.com/p/581346498
1、适配器模式
详细见我的教程,介绍了4种适配器:适配器类的方法,适配器的方法,缺省适配器,双向适配器
https://www.toutiao.com/article/7294089494251241993/
1.1作用
将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作
1.2结构图
(1)
(2)下面这个图说的更明白
1.3简单例子
下面是最简单的适配器类的方法,其它方法见我的教程
(1)代码
//(1)目标抽象类,我们现在系统中定义的接口,不想改,不能改了
interface Target
{
void Request();
}
//(2)适配者类,我们以前编写的代码,挺好用的,但是现在的接口无法直接用它
class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine("我是一个功能完备,已经编写好却无法直接使用的适配者类");
Console.WriteLine("我现在跑起来了!");
}
}
//(3)适配器类,我们的转换接口,让现在的接口能用我们以前编写的挺好的代码
class Adapter : Adaptee, Target
{
public void Request()
{
base.SpecificRequest();
}
}
class Client
{
public void Run()
{
//(4.1)定义转换器Adapter
Target adapter = new Adapter();
//(4.2)直接调用转换接口的实现
adapter.Request();
}
}
class demo
{
static void Main(string[] args)
{
Client client = new Client();
client.Run();
}
}
(2)测试结果
2、桥接模式
详见我的教程
https://www.toutiao.com/article/7296018590821040659/
2.1作用
桥接模式,将抽象部分与它的实现部分分离,使它们都可以独立地变化。
Bridge模式的应用一般在“两个非常强的变化维度”;有时候即使有两个变化的维度;但是某个方向的变化维度并不剧烈——换言之两个变化不会导致纵横交错的结果,并不一定要使用Bridge模式。
2.2结构图
(1)
(2)描述的更详细
2.3简单代码例子
(1)图形例子
(2)代码如下
using System.Drawing;
//(1)Abstraction
public abstract class Shape
{
//形状内部包含了另一个维度:color
protected IColor color;
public void SetColor(IColor color)
{
this.color = color;
}
//设置形状
public abstract void Draw();
}
//(2.1)RefinedAbstraction
public class Circle : Shape
{
public override void Draw()
{
color.Paint("圆形");
}
}
//(2.2)RefinedAbstraction
public class Rectangle : Shape
{
public override void Draw()
{
color.Paint("长方形");
}
}
//(2.3)RefinedAbstraction
public class Triangle : Shape
{
public override void Draw()
{
color.Paint("三角形");
}
}
//(3)Implementor
public interface IColor
{
void Paint(string shape);
}
//(3.1)ConcreteImplementor
public class Blue : IColor
{
public void Paint(string shape)
{
Console.WriteLine(#34;蓝色的{shape}");
}
}
//(3.2)ConcreteImplementor
public class Yellow : IColor
{
public void Paint(string shape)
{
Console.WriteLine(#34;黄色的{shape}");
}
}
//(3.3)ConcreteImplementor
public class Red : IColor
{
public void Paint(string shape)
{
Console.WriteLine(#34;红色的{shape}");
}
}
public class Client
{
public void Create()
{
Shape circle = new Circle();
IColor blue = new Blue();
circle.SetColor(blue);//设置颜色
circle.Draw();//画图
Shape triangle = new Triangle();
triangle.SetColor(blue);
triangle.Draw();
}
}
class Demo
{
static void Main(string[] args)
{
Client client = new Client();
client.Create();
}
}
(3)测试结果
2.4适用场合
- 如果一个系统需要在构件的抽象化角色和具体化角色之间添加更多的灵活性;避免在两个层次之间建立静态的联系。
- 设计要求实现化角色的任何改变不应当影响客户端;或者实现化角色的改变对客户端是完全透明的。
- 需要跨越多个平台的图形和窗口系统上。
- 一个类存在两个独立变化的维度;且两个维度都需要进行扩展。
3组合模式
详细见我的教程
https://www.toutiao.com/article/7296660978903745043/
3.1作用
组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
PS:树状结构的创建、搜索等应用
3.2结构图
(1)
(2)
3.3例子
(1)例子描述
Unity(三维动画很好用的一个软件)中的一个Hierarchy层级图,根节点是Root,下面有GameObject,GameObject (1), GameObject (2)三个节点,其中GameObject (1)下又有GameObject_A和GameObject_B两个节点。
(2)代码
// 抽象组件类
public abstract class DMComponent
{
protected string mName;
public string Name { get { return mName; } }
protected List<DMComponent> mChildren;
public DMComponent(string name)
{
mName = name;
mChildren = new List<DMComponent>();
}
public List<DMComponent> Children { get { return mChildren; } }
public abstract void AddChild(DMComponent c);
public abstract void RemoveChild(DMComponent c);
public abstract DMComponent GetChild(int index);
}
// 叶子组件类
public class DMLeaf : DMComponent
{
public DMLeaf(string name) : base(name) { }
public override void AddChild(DMComponent c)
{
return;
}
public override void RemoveChild(DMComponent c)
{
return;
}
public override DMComponent? GetChild(int index)
{
return null;
}
}
// 容器组件类
public class DMComposite : DMComponent
{
public DMComposite(string name) : base(name) { }
public override void AddChild(DMComponent c)
{
mChildren.Add(c);
}
public override void RemoveChild(DMComponent c)
{
mChildren.Remove(c);
}
public override DMComponent GetChild(int index)
{
return mChildren[index];
}
}
//搜索工具类
public class SearchHelper
{
// 广度优先检索
public static void BreadthFirstSearch(DMComponent component)
{
Queue<DMComponent> q = new Queue<DMComponent>();
q.Enqueue(component);
Console.WriteLine(component.Name);
while (q.Count > 0)
{
DMComponent temp = q.Dequeue();
List<DMComponent> children = temp.Children;
foreach (DMComponent child in children)
{
Console.WriteLine(child.Name);
q.Enqueue(child);
}
}
}
// 深度优先检索
public static void DepthFirstSearch(DMComponent component)
{
Console.WriteLine(component.Name);
List<DMComponent> children = component.Children;
if (children == null || children.Count == 0) return;
foreach (DMComponent child in children)
{
DepthFirstSearch(child);
}
}
}
public class Client
{
public void Run()
{
DMComponent root = new DMComposite("Root");
// 添加Root下的三个节点
DMLeaf leaf1 = new DMLeaf("GameObject");
DMLeaf leaf2 = new DMLeaf("GameObject (2)");
DMComponent gameObject1 = new DMComposite("GameObject (1)");
root.AddChild(leaf1);
root.AddChild(gameObject1);
root.AddChild(leaf2);
// 添加GameObject (1)下面的两个节点
DMLeaf child1 = new DMLeaf("GameObject_A");
DMLeaf child2 = new DMLeaf("GameObject_B");
gameObject1.AddChild(child1);
gameObject1.AddChild(child2);
// 按照广度优先或深度优先输出节点顺序
Console.WriteLine("----深度优先检索-----------");
SearchHelper.DepthFirstSearch(root);
Console.WriteLine("----广度优先检索-----------");
SearchHelper.BreadthFirstSearch(root);
//Console.ReadLine();
}
}
public class Demo {
public static void Main()
{
Client client = new Client();
client.Run();
}
}
(3)测试结果
3.4适用范围
- Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化为“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个对象,还是组合的对象容器。
- 将“客户代码与复杂的对象容器结构”解耦是Composite模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口--而非对象容器的复杂内部实现结构--发生依赖关系,从而更能“应对变化”
- Composite模式中,是将“Add”和“Remove”等和对象容器相关的方法定义在“表示抽象对象的Compoent类”中,还是将其定义在“表示对象容器的Composite类中”,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。这里有可能违背面向对象的“单一职责原则”,当时对于这种特殊结构,则又是必须付出的代价。
- Composite模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历要求,可使用缓存技巧来改善效率。
4、装饰模式
4.1作用
装饰模式,动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
装饰器模式旨在动态地向现有对象添加附加功能。装饰器为扩展功能提供了子类化的替代方法。虽然可以通过对象类的子类化来为整个对象类添加功能,但装饰器模式旨在仅向单个对象添加功能,而使该类的其他对象保持不变。
4.2结构图
(1)
(2)
4.3、简易的例子
(1)代码
// (1)Component 接口
public interface IShape
{
void Draw();
}
//(2) ConcreteComponent 类
public class Rectangle : IShape
{
public void Draw()
{
Console.WriteLine("Drawing a rectangle.");
}
}
// (3)Decorator 类
public abstract class ShapeDecorator : IShape
{
protected IShape decoratedShape;
public ShapeDecorator(IShape decoratedShape)
{
this.decoratedShape = decoratedShape;
}
public virtual void Draw()
{
decoratedShape.Draw();
}
}
//(4) ConcreteDecorator 类1
public class RedShapeDecorator : ShapeDecorator
{
public RedShapeDecorator(IShape decoratedShape) : base(decoratedShape)
{
}
public override void Draw()
{
decoratedShape.Draw();
//具体功能扩展测试代码如下
Console.WriteLine("Border Color: Red_1");
}
}
//(5) ConcreteDecorator 类2
public class BlueShapeDecorator : ShapeDecorator
{
public BlueShapeDecorator(IShape decoratedShape) : base(decoratedShape)
{
}
public override void Draw()
{
decoratedShape.Draw();
//具体功能扩展测试代码如下
Console.WriteLine("Border Color: blue2_1");
}
}
// 客户端代码
public class Client
{
public void run()
{
// (1)创建一个具体的对象
IShape rectangle = new Rectangle();
// (2)用装饰器类动态地给它添加一些额外的行为
IShape redRectangle = new RedShapeDecorator(rectangle);
// (3)原始具体类的输出
rectangle.Draw();
Console.WriteLine("-----------------------------");
// (4)用具体类装饰一下输出
redRectangle.Draw();
Console.WriteLine("-----------------------------");
// (5)注意啦,对上面的装饰器结果接着进行装饰!!!
IShape blueShapeDecorator = new BlueShapeDecorator(redRectangle);
blueShapeDecorator.Draw();
}
}
public class Demo
{
static void Main(string[] args)
{
Client client = new Client();
client.run();
}
}
第(3)个Decorator装饰类定义中,实现了最上面上面的接口,注意:
- 继承了接口,
- 类里面有protected属性的接口字段
- 使用虚函数virtual实现接口中的方法。(如果接口使用抽象类定义,则可以重写,并使用相同的方法实现)
(2)测试结果
4.3应用特点
- (1)接口(抽象类)定义了规范
- (2)具体类A实现了基本功能
- (3)装饰器(抽象类)对接口进行了实现,并定义了protected的接口字段
- (4)装饰器的具体类对接口功能进行了拓展
- (5)客户端通过创建具体类A;在装饰器具体类A中,并在构造函数传入具体类A;从而拓展了具体类A的扩展功能
- (6)客户端还可以创建装饰器具体类B,并在构造函数传入装饰器具体类A,接着扩展了装饰器A,从而实现了2个扩展功能
- (7)装饰器可以一路装饰下去。
5、外观模式
详细内容可参考我的教程
https://www.toutiao.com/article/7297861371478393353/
5.1作用
外观模式,为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
右边的图外部客户程序不再直接与子系统内部类直接交互,而是通过一个Fa?ade接口实现内外系统之间的关联,大大降低了系统的耦合性。
5.2结构图
(1)
(2)
5.3简单例子
using System;
namespace Facade
{
public class Television
{
public void TurnOn()
{
Console.WriteLine("Turning on the television");
}
public void TurnOff()
{
Console.WriteLine("Turning off the television");
}
}
public class SoundSystem
{
public void TurnOn()
{
Console.WriteLine("Turning on the sound system");
}
public void TurnOff()
{
Console.WriteLine("Turning off the sound system");
}
}
public class DVDPlayer
{
public void TurnOn()
{
Console.WriteLine("Turning on the DVD player");
}
public void TurnOff()
{
Console.WriteLine("Turning off the DVD player");
}
}
public class HomeTheaterFacade
{
private Television tv;
private SoundSystem ss;
private DVDPlayer dvd;
public HomeTheaterFacade(Television television, SoundSystem soundSystem, DVDPlayer dvdPlayer)
{
tv = television;
ss = soundSystem;
dvd = dvdPlayer;
}
public void WatchMovie()
{
Console.WriteLine("Get ready to watch a movie");
tv.TurnOn();
ss.TurnOn();
dvd.TurnOn();
}
public void EndMovie()
{
Console.WriteLine("Ending the movie");
tv.TurnOff();
ss.TurnOff();
dvd.TurnOff();
}
}
5.4、前面讲的几个结构型设计模式的区别
- (1)外观模式(Facade)注重简化接口
- (2)适配器模式(Adapter)模式注重转换接口
- (3)桥接模式(Bridge)注重分离接口(抽象)与其实现(或者说两个维度的变化分离)
- (4)装饰器模式(Decorator)注重稳定接口的前提下为对象扩展功能。
6享元模式
详细内容参考我的教程
https://www.toutiao.com/article/7298532303830106661/
6.1作用
享元模式,运用共享技术有效地支持大量细粒度的对象。(《设计模式》GoF)
但是,在某些特殊的应用中,由于对象的数量太大,采用面向对象会给系统带来难以承受的内存开销,比如图形应用中的图元等对象、字处理应用中的字符对象等。
6.2结构图
(1)
(2)
6.3简单例子
(1)测试代码
// (1)定义享元接口
public interface IShape
{
void Draw(int x, int y);
}
// (2)实现具体享元类
public class Circle : IShape
{
private string _shape;
public Circle()
{
_shape = "circle";
}
public void Draw(int x, int y)
{
Console.WriteLine("Draw {0} at ({1}, {2})", _shape, x, y);
}
}
//(3)定义享元工厂类
public class ShapeFactory
{
private Dictionary<string, IShape> _shapes;
public ShapeFactory()
{
_shapes = new Dictionary<string, IShape>();
}
public IShape GetShape(string shape)
{
if (!_shapes.ContainsKey(shape))
{
_shapes[shape] = new Circle();
}
return _shapes[shape];
}
}
//客户端
public class Client
{
public void Run()
{
ShapeFactory factory = new ShapeFactory();
IShape circle1 = factory.GetShape("circle");
IShape circle2 = factory.GetShape("circle");
IShape circle3 = factory.GetShape("circle");
circle1.Draw(0, 0);
circle2.Draw(10, 10);
circle3.Draw(20, 20);
}
}
class Demo
{
public static void Main()
{
Client client = new Client();
client.Run();
}
}
(2)测试结果
6.4、Flyweight模式的几个要点
注意:一般使用了Dictionary或者Hashtable来实现
- 面向对象很好地解决了抽象性的问题,但是作为一个运行在机器中的程序实体,我们需要考虑对象的代价问题。Flyweight设计模式主要解决面向对象的代价问题,一般不触及面向对象的抽象性问题。
- Flyweight采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。在具体实现方面,要注意对象状态的处理。
- 对象的数量太大从而导致对象内存开销加大--什么样的数量才算大?这需要我们仔细的根据具体应用情况进行评估,而不能凭空臆断。
7、代理模式
详细见我的教程:
https://www.toutiao.com/article/7298608338068226614/
7.1作用
代理模式,为其他对象提供一种代理以控制对这个对象的访问。
人们对于复杂的软件系统常常有一种处理方法,即增加一层间接层,从而对系统获得一种更为灵活、满足特定需求的解决方案。
7.2结构图
(1)
(2)
(3)
7.3简易例子
(1)代码
//(1)远程端的数学运算接口例子
public interface IMath
{
double Add(double x, double y);
double Sub(double x, double y);
double Mul(double x, double y);
double Div(double x, double y);
}
//(2)远程端的数学运算例子的具体实现
public class Math : IMath
{
public double Add(double x, double y)
{
return x + y;
}
public double Sub(double x, double y)
{
return x - y;
}
public double Mul(double x, double y)
{
return x * y;
}
public double Div(double x, double y)
{
return x / y;
}
}
//(3)运行于本地的算法代理的例子
//实际真实例子中可通过web交互
public class MathProxy : IMath
{
private Math _math = new Math();
public double Add(double x, double y)
{
return _math.Add(x, y);
}
public double Sub(double x, double y)
{
return _math.Sub(x, y);
}
public double Mul(double x, double y)
{
return _math.Mul(x, y);
}
public double Div(double x, double y)
{
return _math.Div(x, y);
}
}
//(4)运行于本地的client
class Demo
{
static void Main(string[] args)
{
MathProxy proxy = new MathProxy();
Console.WriteLine("3 + 2 = " + proxy.Add(3, 2));
Console.WriteLine("4 - 2 = " + proxy.Sub(4, 2));
Console.WriteLine("5 * 2 = " + proxy.Mul(5, 2));
Console.WriteLine("6 / 2 = " + proxy.Div(6, 2));
}
}
(2)测试结果
7.4 Proxy模式的几个要点
- “增加一层间接层”是软件系统中对许多复杂问题的一种常见解决方法。在面向对象系统中,直接使用某些对象会带来很多问题,作为间接层的proxy对象便是解决这一问题的常用手段。
- 具体proxy设计模式的实现方法、实现粒度都相差很大,有些可能对单个对象做细粒度的控制,如copy-on-write技术,有些可能对组件模块提供抽象代理层,在架构层次对对象做proxy。
- Proxy并不一定要求保持接口的一致性,只要能够实现间接控制,有时候损及一些透明性是可以接受的。