建造者模式、组合模式和装饰者模式
今天将和大家一起讨论建造者模式、组合模式和装饰者模式。
先来讨论建造者模式,字里行间很容易明白这是一种对象创建型模式,一般对象的创建是由本身抽象实例化本身,在这里需要通过另外种角色建造者角色去帮我们创建。这是为什么呢?因为在现实系统中,我们遇到的对象往往是很复杂的对象。该对象由很多子对象组成,并且这些子对象的创建步骤是一样,只是它们的创建细节不一样,我们怎么解决该类问题呢!这就要求我们使用“封装机制”来隔离出“复杂对象的各个部分”的变化,这就是要讨论的建造者模式。
结构示意图如下:
建造者模式中的角色:
指导角色:对象创建的指导者,负责将客户的请求传给建造者,控制建造者怎样创建复杂对象。
抽象建造者角色:复杂对象创建者,提供对象创建模板。
具体建造者角色:实现复杂对象创建,提供对象创建细节,并向客户返回复杂对象。
示意代码:
抽象建造者: /// <summary> /// 抽象构造者,制定了复杂对象的构造步骤 /// </summary> public abstract class AbstractBuilder { public abstract void BuilderPart1(); public abstract void BuilderPart2(); public abstract Food GetFood(); } 具体建造者: /// <summary> /// 具体建造者,实现了复杂对象具体构造细节 /// </summary> public class FoodBuilder:AbstractBuilder { private Food food = new Food(); public override void BuilderPart1() { food.AddFood("a", "1"); } public override void BuilderPart2() { food.AddFood("b", "2"); } public override Food GetFood() { return food; } } 复杂对象: /// <summary> /// 复杂对象 /// </summary> public class Food { private Dictionary<string, string> ht = new Dictionary<string, string>(); public void AddFood(string name, string price) { ht.Add(name, price); } public void Show() { foreach (KeyValuePair<string, string> kv in ht) { Console.WriteLine(kv.Key + ":" + kv.Value); } } } 指挥者: /// <summary> /// 复杂对象构造控制者 /// </summary> public class Director { public Director() { } public Food Construct(AbstractBuilder ab) { ab.BuilderPart1(); ab.BuilderPart2(); return ab.GetFood(); } } 客户端示意性代码: class Client { static void Main(string[] args) { AbstractBuilder ab = new FoodBuilder(); Director d = new Director(); Food food=d.Construct(ab); food.Show(); Console.ReadLine(); } }
复杂对象创建时序图:
总结:
建造者模式向客户端隐藏了复杂对象的构造细节,符合设计原则开-闭原则。
接下来我们讨论组合模式(Composite)
在学习ASP.NET的时候,我们经常会碰到页面生命周期的问题。相信大家用户控件都用过,
当服务器渲染用户控件的时候,都会调用其中子空间的渲染方法,在用户(我们)看来,使用用户控件这种复杂元素就像使用简单元素一样方便,这无形之中就用到了常用的设计思想组合模式。组合模式的作用:就是封装复杂元素的复杂性,提供和简单元素一样的访问接口。
结构示意图:
组合模式涉及的角色:
抽象构件:规范了简单对象和复杂对象共同的接口。
简单对象:实现了抽象构件。
复杂对象:实现了抽象构件,并且提供管理简单对象的方法。
这里组合对象既实现了一般构件的方法,并且拥有复杂构件的维护接口,这属于组合模式安全式实现,如果将这部分接口提取到抽象构件里面,那么简单构件也拥有这部分功能,这是组合模式的透明式实现,但是这种设计有点不合理。
组合模式安全模式实现示意代码:
抽象构件: /// <summary> /// 抽象构件 定义了简单构件和复杂构件统一的访问接口 /// </summary> public abstract class AbstractComponent { public abstract void Render(); } 简单构件: /// <summary> /// 简单构件 /// </summary> public class SampleComponent:AbstractComponent { public override void Render() { //render code } } 复杂构件: /// <summary> /// 组合构件,聚合了简单构件 /// </summary> public class Composite:AbstractComponent { private List<AbstractComponent> sl=new List<AbstractComponent>(); public override void Render() { foreach (AbstractComponent ac in sl) { ac.Render(); } } public void Add(AbstractComponent ac) { sl.Add(ac); } public void Remove(AbstractComponent ac) { sl.Remove(ac); } } 客户端示意代码: /// <summary> /// 客户端 /// </summary> class Client { static void Main(string[] args) { Composite c = new Composite(); c.Add(new SampleComponent()); //渲染复杂构件 c.Render(); } }
总结:
组合模式解耦了客户程序与复杂元素内部元素内部结构。
最后我们讨论下装饰者模式(decoration)
“装饰”二字在生活中经常听到。装饰房子简称装潢。女人最喜欢的一件事就是“装饰”(化妆)。装饰者模式就是给已有的对象增加新功能,但不是去修改原来的代码,就是借助于装饰者对象。它的魅力体现在对象A装饰成对象B还可以装饰成对象C…,可以以此装饰下去,这样被装饰的对象就拥有了所有装饰者对象的能力。还有个比较灵活的地方就是装饰对象可以任意组合装饰被装饰对象。
装饰者模式结构图:
设计到的角色:
抽象构件:装饰对象与被装饰对象统一接口。
具体构件:被装饰的对象。
抽象装饰者角色:包含抽象构件,实现统一接口。
具体装饰者角色:为具体构件提供新的职责。
示意性代码:
抽象构件: /// <summary> /// 抽象构件 /// </summary> public abstract class Component { public abstract void Operation(); } 具体构件: /// <summary> /// 具体构件 需要被装饰的对象 /// </summary> public class ConcreteComponent:Component { public override void Operation() { //operate code Console.WriteLine("ConcreteComponent!"); } } 抽象装饰者: /// <summary> /// 装饰者接口,继承自抽象构件,并包含抽象构件引用 /// </summary> public class Decorator:Component { private Component component; public Decorator(Component component) { this.component = component; } public override void Operation() { component.Operation(); } } 具体装饰者: /// <summary> /// 具体装饰者 实现装饰者统一接口,并加入新的职责 /// </summary> public class Decorator1:Decorator { public Decorator1(Component component) : base(component) { } public override void Operation() { base.Operation(); AddNewBehavior(); } private void AddNewBehavior() { Console.WriteLine("Decorator1"); } } 客户端代码: class Client { static void Main(string[] args) { ConcreteComponent cc = new ConcreteComponent(); Decorator d = new Decorator1(cc); d.Operation(); Console.ReadLine(); } }
总结:
装饰模式与继承都是为了增加对象的功能,但是装饰者模式比继承更灵活,尽量用组合代替继承。