0%

Template Method,模板方法:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,TemplateMethod使得子类可以不改变一个算法的结构即可以重定义该算法的某些特定步骤。 应用场景:一个操作的步骤稳定,而具体细节的改变延迟的子类

http://www.cnblogs.com/zhili/p/TemplateMethodPattern.html
http://www.cnblogs.com/PatrickLiu/p/7837716.html

  在上一篇文章介绍到可以使用状态者模式和观察者模式来解决中介者模式存在的问题,在本文中将首先通过一个银行账户的例子来解释状态者模式,通过这个例子使大家可以对状态者模式有一个清楚的认识,接着,再使用状态者模式来解决上一篇文章中提出的问题。

  每个对象都有其对应的状态,而每个状态又对应一些相应的行为,如果某个对象有多个状态时,那么就会对应很多的行为。那么对这些状态的判断和根据状态完成的行为,就会导致多重条件语句,并且如果添加一种新的状态时,需要更改之前现有的代码。这样的设计显然违背了开闭原则。状态模式正是用来解决这样的问题的。状态模式将每种状态对应的行为抽象出来成为单独新的对象,这样状态的变化不再依赖于对象内部的行为。

2.1 状态者模式的定义

  上面对状态模式做了一个简单的介绍,这里给出状态模式的定义。

  状态模式——允许一个对象在其内部状态改变时自动改变其行为,对象看起来就像是改变了它的类。

2.2 状态者模式的结构

  既然状态者模式是对已有对象的状态进行抽象,则自然就有抽象状态者类和具体状态者类,而原来已有对象需要保存抽象状态者类的引用,通过调用抽象状态者的行为来改变已有对象的行为。经过上面的分析,状态者模式的结构图也就很容易理解了,具体结构图如下图示。

  

  从上图可知,状态者模式涉及以下三个角色:

  • Account类:维护一个State类的一个实例,该实例标识着当前对象的状态。
  • State类:抽象状态类,定义了一个具体状态类需要实现的行为约定。
  • SilveStater、GoldState和RedState类:具体状态类,实现抽象状态类的每个行为。

2.3 状态者模式的实现

  下面,就以银行账户的状态来实现下状态者模式。银行账户根据余额可分为RedState、SilverState和GoldState。这些状态分别代表透支账号,新开账户和标准账户。账号余额在【-100.0,0.0】范围表示处于RedState状态,账号余额在【0.0 , 1000.0】范围表示处于SilverState,账号在【1000.0, 100000.0】范围表示处于GoldState状态。下面以这样的一个场景实现下状态者模式,具体实现代码如下所示:

复制代码

1 namespace StatePatternSample 2 {
3 public class Account 4 {
5 public State State {get;set;}
6 public string Owner { get; set; }
7 public Account(string owner) 8 {
9 this.Owner = owner; 10 this.State = new SilverState(0.0, this);
11 }
12
13 public double Balance { get {return State.Balance; }} // 余额 14 // 存钱
15 public void Deposit(double amount) 16 {
17 State.Deposit(amount);
18 Console.WriteLine(“存款金额为 {0:C}——“, amount);
19 Console.WriteLine(“账户余额为 =:{0:C}”, this.Balance);
20 Console.WriteLine(“账户状态为: {0}”, this.State.GetType().Name);
21 Console.WriteLine();
22 }
23
24 // 取钱
25 public void Withdraw(double amount) 26 {
27 State.Withdraw(amount);
28 Console.WriteLine(“取款金额为 {0:C}——“,amount);
29 Console.WriteLine(“账户余额为 =:{0:C}”, this.Balance);
30 Console.WriteLine(“账户状态为: {0}”, this.State.GetType().Name);
31 Console.WriteLine();
32 }
33
34 // 获得利息
35 public void PayInterest() 36 {
37 State.PayInterest();
38 Console.WriteLine(“Interest Paid — “);
39 Console.WriteLine(“账户余额为 =:{0:C}”, this.Balance);
40 Console.WriteLine(“账户状态为: {0}”, this.State.GetType().Name);
41 Console.WriteLine();
42 }
43 }
44
45 // 抽象状态类
46 public abstract class State 47 {
48 // Properties
49 public Account Account { get; set; }
50 public double Balance { get; set; } // 余额
51 public double Interest { get; set; } // 利率
52 public double LowerLimit { get; set; } // 下限
53 public double UpperLimit { get; set; } // 上限
54
55 public abstract void Deposit(double amount); // 存款
56 public abstract void Withdraw(double amount); // 取钱
57 public abstract void PayInterest(); // 获得的利息
58 }
59
60 // Red State意味着Account透支了
61 public class RedState : State 62 {
63 public RedState(State state) 64 {
65 // Initialize
66 this.Balance = state.Balance; 67 this.Account = state.Account; 68 Interest = 0.00;
69 LowerLimit = -100.00;
70 UpperLimit = 0.00;
71 }
72
73 // 存款
74 public override void Deposit(double amount) 75 {
76 Balance += amount; 77 StateChangeCheck();
78 }
79 // 取钱
80 public override void Withdraw(double amount) 81 {
82 Console.WriteLine(“没有钱可以取了!”);
83 }
84
85 public override void PayInterest() 86 {
87 // 没有利息
88 }
89
90 private void StateChangeCheck() 91 {
92 if (Balance > UpperLimit) 93 {
94 Account.State = new SilverState(this);
95 }
96 }
97 }
98
99 // Silver State意味着没有利息得
100 public class SilverState :State 101 { 102 public SilverState(State state) 103 : this(state.Balance, state.Account) 104 { 105 } 106
107 public SilverState(double balance, Account account) 108 { 109 this.Balance = balance; 110 this.Account = account; 111 Interest = 0.00; 112 LowerLimit = 0.00; 113 UpperLimit = 1000.00; 114 } 115
116 public override void Deposit(double amount) 117 { 118 Balance += amount; 119 StateChangeCheck(); 120 } 121 public override void Withdraw(double amount) 122 { 123 Balance -= amount; 124 StateChangeCheck(); 125 } 126
127 public override void PayInterest() 128 { 129 Balance += Interest * Balance; 130 StateChangeCheck(); 131 } 132
133 private void StateChangeCheck() 134 { 135 if (Balance < LowerLimit) 136 { 137 Account.State = new RedState(this); 138 } 139 else if (Balance > UpperLimit) 140 { 141 Account.State = new GoldState(this); 142 } 143 } 144 } 145
146 // Gold State意味着有利息状态
147 public class GoldState : State 148 { 149 public GoldState(State state) 150 { 151 this.Balance = state.Balance; 152 this.Account = state.Account; 153 Interest = 0.05; 154 LowerLimit = 1000.00; 155 UpperLimit = 1000000.00; 156 } 157 // 存钱
158 public override void Deposit(double amount) 159 { 160 Balance += amount; 161 StateChangeCheck(); 162 } 163 // 取钱
164 public override void Withdraw(double amount) 165 { 166 Balance -= amount; 167 StateChangeCheck(); 168 } 169 public override void PayInterest() 170 { 171 Balance += Interest * Balance; 172 StateChangeCheck(); 173 } 174
175 private void StateChangeCheck() 176 { 177 if (Balance < 0.0) 178 { 179 Account.State = new RedState(this); 180 } 181 else if (Balance < LowerLimit) 182 { 183 Account.State = new SilverState(this); 184 } 185 } 186 } 187
188 class App 189 { 190 static void Main(string[] args) 191 { 192 // 开一个新的账户
193 Account account = new Account(“Learning Hard”); 194
195 // 进行交易 196 // 存钱
197 account.Deposit(1000.0); 198 account.Deposit(200.0); 199 account.Deposit(600.0); 200
201 // 付利息
202 account.PayInterest(); 203
204 // 取钱
205 account.Withdraw(2000.00); 206 account.Withdraw(500.00); 207
208 // 等待用户输入
209 Console.ReadKey(); 210 } 211 } 212 }

复制代码

  上面代码的运行结果如下图所示:

  从上图可以发现,进行存取款交易,会影响到Account内部的状态,由于状态的改变,从而影响到Account类行为的改变,而且这些操作都是发生在运行时的。

  在上一篇博文中,我曾介绍到中介者模式存在的问题,详细的问题描述可以参考上一篇博文。下面利用观察者模式和状态者模式来完善中介者模式,具体的实现代码如下所示:

1 // 抽象牌友类
2 public abstract class AbstractCardPartner 3 {
4 public int MoneyCount { get; set; }
5
6 public AbstractCardPartner() 7 {
8 MoneyCount = 0;
9 }
10
11 public abstract void ChangeCount(int Count, AbstractMediator mediator); 12 }
13
14 // 牌友A类
15 public class ParterA : AbstractCardPartner 16 {
17 // 依赖与抽象中介者对象
18 public override void ChangeCount(int Count, AbstractMediator mediator) 19 {
20 mediator.ChangeCount(Count);
21 }
22 }
23
24 // 牌友B类
25 public class ParterB : AbstractCardPartner 26 {
27 // 依赖与抽象中介者对象
28 public override void ChangeCount(int Count, AbstractMediator mediator) 29 {
30 mediator.ChangeCount(Count);
31 }
32 }
33
34 // 抽象状态类
35 public abstract class State 36 {
37 protected AbstractMediator meditor; 38 public abstract void ChangeCount(int count); 39 }
40
41 // A赢状态类
42 public class AWinState : State 43 {
44 public AWinState(AbstractMediator concretemediator) 45 {
46 this.meditor = concretemediator; 47 }
48
49 public override void ChangeCount(int count) 50 {
51 foreach (AbstractCardPartner p in meditor.list) 52 {
53 ParterA a = p as ParterA; 54 //
55 if (a != null)
56 {
57 a.MoneyCount += count; 58 }
59 else
60 {
61 p.MoneyCount -= count; 62 }
63 }
64 }
65 }
66
67 // B赢状态类
68 public class BWinState : State 69 {
70 public BWinState(AbstractMediator concretemediator) 71 {
72 this.meditor = concretemediator; 73 }
74
75 public override void ChangeCount(int count) 76 {
77 foreach (AbstractCardPartner p in meditor.list) 78 {
79 ParterB b = p as ParterB; 80 // 如果集合对象中时B对象,则对B的钱添加
81 if (b != null)
82 {
83 b.MoneyCount += count; 84 }
85 else
86 {
87 p.MoneyCount -= count; 88 }
89 }
90 }
91 }
92
93 // 初始化状态类
94 public class InitState : State 95 {
96 public InitState() 97 {
98 Console.WriteLine(“游戏才刚刚开始,暂时还有玩家胜出”);
99 } 100
101 public override void ChangeCount(int count) 102 { 103 //
104 return; 105 } 106 } 107
108 // 抽象中介者类
109 public abstract class AbstractMediator 110 { 111 public List list = new List(); 112
113 public State State { get; set; } 114
115 public AbstractMediator(State state) 116 { 117 this.State = state; 118 } 119
120 public void Enter(AbstractCardPartner partner) 121 { 122 list.Add(partner); 123 } 124
125 public void Exit(AbstractCardPartner partner) 126 { 127 list.Remove(partner); 128 } 129
130 public void ChangeCount(int count) 131 { 132 State.ChangeCount(count); 133 } 134 } 135
136 // 具体中介者类
137 public class MediatorPater : AbstractMediator 138 { 139 public MediatorPater(State initState) 140 : base(initState) 141 { } 142 } 143
144 class Program 145 { 146 static void Main(string[] args) 147 { 148 AbstractCardPartner A = new ParterA(); 149 AbstractCardPartner B = new ParterB(); 150 // 初始钱
151 A.MoneyCount = 20; 152 B.MoneyCount = 20; 153
154 AbstractMediator mediator = new MediatorPater(new InitState()); 155
156 // A,B玩家进入平台进行游戏
157 mediator.Enter(A); 158 mediator.Enter(B); 159
160 // A赢了
161 mediator.State = new AWinState(mediator); 162 mediator.ChangeCount(5); 163 Console.WriteLine(“A 现在的钱是:{0}”, A.MoneyCount);// 应该是25
164 Console.WriteLine(“B 现在的钱是:{0}”, B.MoneyCount); // 应该是15 165
166 // B 赢了
167 mediator.State = new BWinState(mediator); 168 mediator.ChangeCount(10); 169 Console.WriteLine(“A 现在的钱是:{0}”, A.MoneyCount);// 应该是25
170 Console.WriteLine(“B 现在的钱是:{0}”, B.MoneyCount); // 应该是15
171 Console.Read(); 172 } 173 }

View Code

   在以下情况下可以考虑使用状态者模式。

  • 当一个对象状态转换的条件表达式过于复杂时可以使用状态者模式。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简单化。
  • 当一个对象行为取决于它的状态,并且它需要在运行时刻根据状态改变它的行为时,就可以考虑使用状态者模式。

   状态者模式的主要优点是:

  • 将状态判断逻辑每个状态类里面,可以简化判断的逻辑。
  • 当有新的状态出现时,可以通过添加新的状态类来进行扩展,扩展性好。

  状态者模式的主要缺点是:

  • 如果状态过多的话,会导致有非常多的状态类,加大了开销。

  状态者模式是对对象状态的抽象,从而把对象中对状态复杂的判断逻辑已到各个状态类里面,从而简化逻辑判断。在下一篇文章将分享我对策略模式的理解。

一、引言

  这个系列也是自己对设计模式的一些学习笔记,希望对一些初学设计模式的人有所帮助的,在上一个专题中介绍了单例模式,在这个专题中继续为大家介绍一个比较容易理解的模式——简单工厂模式。

二、简单工厂模式的介绍

  说到简单工厂,自然的第一个疑问当然就是什么是简单工厂模式了? 在现实生活中工厂是负责生产产品的,同样在设计模式中,简单工厂模式我们也可以理解为负责生产对象的一个类, 我们平常编程中,当使用”new”关键字创建一个对象时,此时该类就依赖与这个对象,也就是他们之间的耦合度高,当需求变化时,我们就不得不去修改此类的源码,此时我们可以运用面向对象(OO)的很重要的原则去解决这一的问题,该原则就是——封装改变,既然要封装改变,自然也就要找到改变的代码,然后把改变的代码用类来封装,这样的一种思路也就是我们简单工厂模式的实现方式了。下面通过一个现实生活中的例子来引出简单工厂模式。

  在外面打工的人,免不了要经常在外面吃饭,当然我们也可以自己在家做饭吃,但是自己做饭吃麻烦,因为又要自己买菜,然而,出去吃饭就完全没有这些麻烦的,我们只需要到餐馆点菜就可以了,买菜的事情就交给餐馆做就可以了,这里餐馆就充当简单工厂的角色,下面让我们看看现实生活中的例子用代码是怎样来表现的。

自己做饭的情况:

复制代码

///


/// 自己做饭的情况 /// 没有简单工厂之前,客户想吃什么菜只能自己炒的 ///

public class Customer
{ ///
/// 烧菜方法 ///

///
///
public static Food Cook(string type)
{
Food food = null; // 客户A说:我想吃西红柿炒蛋怎么办? // 客户B说:那你就自己烧啊 // 客户A说: 好吧,那就自己做吧
if (type.Equals(“西红柿炒蛋”))
{
food = new TomatoScrambledEggs();
} // 我又想吃土豆肉丝, 这个还是得自己做 // 我觉得自己做好累哦,如果能有人帮我做就好了?
else if (type.Equals(“土豆肉丝”))
{
food = new ShreddedPorkWithPotatoes();
} return food;
} static void Main(string[] args)
{ // 做西红柿炒蛋
Food food1 = Cook(“西红柿炒蛋”);
food1.Print();

        Food food2 \= Cook("土豆肉丝");
        food2.Print();

        Console.Read();
    }
} /// <summary>
/// 菜抽象类 /// </summary>
public abstract class Food
{ // 输出点了什么菜
    public abstract void Print();
} /// <summary>
/// 西红柿炒鸡蛋这道菜 /// </summary>
public class TomatoScrambledEggs : Food
{ public override void Print()
    {
        Console.WriteLine("一份西红柿炒蛋!");
    }
} /// <summary>
/// 土豆肉丝这道菜 /// </summary>
public class ShreddedPorkWithPotatoes : Food
{ public override void Print()
    {
        Console.WriteLine("一份土豆肉丝");
    }
}

复制代码

  自己做饭,如果我们想吃别的菜时,此时就需要去买这种菜和洗菜这些繁琐的操作,有了餐馆(也就是简单工厂)之后,我们就可以把这些操作交给餐馆去做,此时消费者(也就是我们)对菜(也就是具体对象)的依赖关系从直接变成的间接的,这样就是实现了面向对象的另一个原则——降低对象之间的耦合度,下面就具体看看有了餐馆之后的实现代码(即简单工厂的实现):

复制代码

///


/// 顾客充当客户端,负责调用简单工厂来生产对象 /// 即客户点菜,厨师(相当于简单工厂)负责烧菜(生产的对象) ///

class Customer
{ static void Main(string[] args)
{ // 客户想点一个西红柿炒蛋
Food food1 = FoodSimpleFactory.CreateFood(“西红柿炒蛋”);
food1.Print(); // 客户想点一个土豆肉丝
Food food2 = FoodSimpleFactory.CreateFood(“土豆肉丝”);
food2.Print();

        Console.Read();
    }
} /// <summary>
/// 菜抽象类 /// </summary>
public abstract class Food
{ // 输出点了什么菜
    public abstract void Print();
} /// <summary>
/// 西红柿炒鸡蛋这道菜 /// </summary>
public class TomatoScrambledEggs : Food
{ public override void Print()
    {
        Console.WriteLine("一份西红柿炒蛋!");
    }
} /// <summary>
/// 土豆肉丝这道菜 /// </summary>
public class ShreddedPorkWithPotatoes : Food
{ public override void Print()
    {
        Console.WriteLine("一份土豆肉丝");
    }
} /// <summary>
/// 简单工厂类, 负责 炒菜 /// </summary>
public class FoodSimpleFactory
{ public static Food CreateFood(string type)
    {
        Food food \= null; if (type.Equals("土豆肉丝"))
        {
            food\= new ShreddedPorkWithPotatoes();
        } else if (type.Equals("西红柿炒蛋"))
        {
            food\= new TomatoScrambledEggs();
        } return food;
    }
}

复制代码

三、优点与缺点

  看完简单工厂模式的实现之后,你和你的小伙伴们肯定会有这样的疑惑(因为我学习的时候也有)——这样我们只是把变化移到了工厂类中罢了,好像没有变化的问题,因为如果客户想吃其他菜时,此时我们还是需要修改工厂类中的方法(也就是多加case语句,没应用简单工厂模式之前,修改的是客户类)。我首先要说:你和你的小伙伴很对,这个就是简单工厂模式的缺点所在(这个缺点后面介绍的工厂方法可以很好地解决),然而,简单工厂模式与之前的实现也有它的优点:

  • 简单工厂模式解决了客户端直接依赖于具体对象的问题,客户端可以消除直接创建对象的责任,而仅仅是消费产品。简单工厂模式实现了对责任的分割。
  • 简单工厂模式也起到了代码复用的作用,因为之前的实现(自己做饭的情况)中,换了一个人同样要去在自己的类中实现做菜的方法,然后有了简单工厂之后,去餐馆吃饭的所有人都不用那么麻烦了,只需要负责消费就可以了。此时简单工厂的烧菜方法就让所有客户共用了。(同时这点也是简单工厂方法的缺点——因为工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都会受到影响,也没什么不好理解的,就如事物都有两面性一样道理

虽然上面已经介绍了简单工厂模式的缺点,下面还是总结下简单工厂模式的缺点:

  • 工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都会受到影响(通俗地意思就是:一旦餐馆没饭或者关门了,很多不愿意做饭的人就没饭吃了)
  • 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,这样就会造成工厂逻辑过于复杂。

了解了简单工厂模式之后的优缺点之后,我们之后就可以知道简单工厂的应用场景了:

  • 当工厂类负责创建的对象比较少时可以考虑使用简单工厂模式()
  • 客户如果只知道传入工厂类的参数,对于如何创建对象的逻辑不关心时可以考虑使用简单工厂模式

四、简单工厂模式UML图

简单工厂模式又叫静态方法模式(因为工厂类都定义了一个静态方法),由一个工厂类根据传入的参数决定创建出哪一种产品类的实例(通俗点表达:通过客户下的订单来负责烧那种菜)。简单工厂模式的UML图如下:

五、.NET中简单工厂模式的实现

  介绍完了简单工厂模式之后,我学习的时候就像:.NET类库中是否有实现了简单工厂模式的类呢?后面确实有,.NET中System.Text.Encoding类就实现了简单工厂模式,该类中的**GetEncoding(int codepage)就是工厂方法,**具体的代码可以通过Reflector反编译工具进行查看,下面我也贴出该方法中部分代码:

public static Encoding GetEncoding(int codepage)
{
Encoding unicode = null; if (encodings != null)
{
unicode = (Encoding) encodings[codepage];
} if (unicode == null)
{ object obj2; bool lockTaken = false; try {
Monitor.Enter(obj2 = InternalSyncObject, ref lockTaken); if (encodings == null)
{
encodings = new Hashtable();
}
unicode = (Encoding) encodings[codepage]; if (unicode != null)
{ return unicode;
} switch (codepage)
{ case 0:
unicode = Default; break; case 1: case 2: case 3: case 0x2a: throw new ArgumentException(Environment.GetResourceString(“Argument_CodepageNotSupported”, new object[] { codepage }), “codepage”); case 0x4b0:
unicode = Unicode; break; case 0x4b1:
unicode = BigEndianUnicode; break; case 0x6faf:
unicode = Latin1; break; case 0xfde9:
unicode = UTF8; break; case 0x4e4:
unicode = new SBCSCodePageEncoding(codepage); break; case 0x4e9f:
unicode = ASCII; break; default:
unicode = GetEncodingCodePage(codepage); if (unicode == null)
{
unicode = GetEncodingRare(codepage);
} break;
}
encodings.Add(codepage, unicode); return unicode;

    }

}

View Code

.NET 中Encoding的UML图为:

Encoding类中实现的简单工厂模式是简单工厂模式的一种演变,此时简单工厂类由抽象产品角色扮演,然而.NET中Encoding类是如何解决简单工厂模式中存在的问题的呢(即如果新添加一种编码怎么办)?在GetEncoding方法里的switch函数有如下代码:

复制代码

switch (codepage)
{
……. default:
unicode = GetEncodingCodePage(codepage); if (unicode == null)
{
unicode = GetEncodingRare(codepage); //当编码很少见时
} break;
……
}

复制代码

  在GetEncodingRare方法里有一些不常用编码的实例化代码,微软正式通过这个方法来解决新增加一种编码的问题。(其实也就是列出所有可能的编码情况),微软之所以以这样的方式来解决这个问题,可能是由于现在编码已经稳定了,添加新编码的可能性比较低,所以在.NET 4.5仍然未改动这部分代码。

六、总结

到这里,简单工厂模式的介绍都到这里了,后面将介绍工厂方法模式来解决简单工厂模式中存在的问题。

本专题中的全部源码:简单工厂模式源码

在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题。创建那么多的对象将会耗费很多的系统资源,它是系统性能提高的一个瓶颈。例如,围棋和五子棋中的黑白棋子,图像中的坐标点或颜色,局域网中的路由器、交换机和集线器,教室里的桌子和凳子等。这些对象有很多相似的地方,如果能把它们相同的部分提取出来共享,则能节省大量的系统资源,这就是享元模式的产生背景。

享元(Flyweight)模式的定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。

享元模式的主要优点是:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。

其主要缺点是:

  • 为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
  • 读取享元模式的外部状态会使得运行时间稍微变长。

享元模式中存在以下两种状态:

  • 内部状态,即不会随着环境的改变而改变的可共享部分。
  • 外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。

模式的结构#

享元模式的主要角色有如下:

  • 抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
  • 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
  • 非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中
  • 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。

下图是享元模式的结构图。图中的 UnsharedConcreteFlyweight 是非享元角色,里面包含了非共享的外部状态信息 info;而 Flyweight 是抽象享元角色,里面包含了享元方法 operation(UnsharedConcreteFlyweight state),非享元的外部状态以参数的形式通过该方法传入;ConcreteFlyweight 是具体享元角色,包含了关键字 key,它实现了抽象享元接口;FlyweightFactory 是享元工厂角色,它逝关键字 key 来管理具体享元;客户角色通过享元工厂获取具体享元,并访问具体享元的相关方法。

模式的实现#

享元模式的实现代码如下:

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
class Program
{
static void Main(string[] args)
{
FlyweightFactory factory=new FlyweightFactory();
IFlyweight f01=factory.GetFlyweight("a");
IFlyweight f02=factory.GetFlyweight("a");
IFlyweight f03=factory.GetFlyweight("a");
IFlyweight f11=factory.GetFlyweight("b");
IFlyweight f12=factory.GetFlyweight("b");
f01.Operation(new UnsharedConcreteFlyweight("第1次调用a。"));
f02.Operation(new UnsharedConcreteFlyweight("第2次调用a。"));
f03.Operation(new UnsharedConcreteFlyweight("第3次调用a。"));
f11.Operation(new UnsharedConcreteFlyweight("第1次调用b。"));
f12.Operation(new UnsharedConcreteFlyweight("第2次调用b。"));
Console.ReadLine();
}
}


public class UnsharedConcreteFlyweight
{
private String info;
public UnsharedConcreteFlyweight(String info)
{
this.info=info;
}
public String GetInfo()
{
return info;
}
public void SetInfo(String info)
{
this.info=info;
}
}


public interface IFlyweight
{
void Operation(UnsharedConcreteFlyweight state);
}


public class ConcreteFlyweight : IFlyweight
{
private String key;
public ConcreteFlyweight(String key)
{
this.key=key;
Console.WriteLine("具体享元"+key+"被创建!");
}
public void Operation(UnsharedConcreteFlyweight outState)
{
Console.WriteLine("具体享元"+key+"被调用,");
Console.WriteLine("非享元信息是:" + outState.GetInfo());
}
}


public class FlyweightFactory
{
private Dictionary<String, IFlyweight> flyweights = new Dictionary<String, IFlyweight>();
public IFlyweight GetFlyweight(String key)
{
IFlyweight flyweight;
flyweights.TryGetValue(key,out flyweight);
if(flyweight!=null)
{
Console.WriteLine("具体享元" + key + "已经存在,被成功获取!");
}
else
{
flyweight=new ConcreteFlyweight(key);
flyweights.Add(key, flyweight);
}
return flyweight;
}
}

程序运行结果如下:

1
2
3
4
5
6
7
8
9
10
具体享元a被创建!
具体享元a已经存在,被成功获取!
具体享元a已经存在,被成功获取!
具体享元b被创建!
具体享元b已经存在,被成功获取!
具体享元a被调用,非享元信息是:第1次调用a。
具体享元a被调用,非享元信息是:第2次调用a。
具体享元a被调用,非享元信息是:第3次调用a。
具体享元b被调用,非享元信息是:第1次调用b。
具体享元b被调用,非享元信息是:第2次调用b。

享元模式是通过减少内存中对象的数量来节省内存空间的,所以以下几种情形适合采用享元模式:

  • 系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源。
  • 大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。
  • 由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式

在前面介绍的享元模式中,其结构图通常包含可以共享的部分和不可以共享的部分。在实际使用过程中,有时候会稍加改变,即存在两种特殊的享元模式:单纯享元模式和复合享元模式,下面分别对它们进行简单介绍。

  • 单纯享元模式:这种享元模式中的所有的具体享元类都是可以共享的,不存在非共享的具体享元类,其结构图如图所示:

  • 复合享元模式:这种享元模式中的有些享元对象是由一些单纯享元对象组合而成的,它们就是复合享元对象。虽然复合享元对象本身不能共享,但它们可以分解成单纯享元对象再被共享,其结构图如图所示:

在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介

主要优点有:

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用
  • 代理对象可以扩展目标对象的功能
  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度

主要缺点有:

  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢
  • 增加了系统的复杂度

代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问。

模式的结构#

代理模式的主要角色如下:

  • 抽象主题(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

class Program
{
static void Main(string[] args)
{

Proxy proxy=new Proxy();
proxy.Request();
Console.ReadKey();
}
}


public interface ISubject
{
void Request();
}


public class RealSubject :ISubject
{
public void Request()
{
Console.WriteLine("访问真实主题方法...");
}
}


public class Proxy : ISubject
{
private RealSubject realSubject;
public void Request()
{
if (realSubject==null)
{
realSubject=new RealSubject();
}
PreRequest();
realSubject.Request();
PostRequest();
}
public void PreRequest()
{
Console.WriteLine("访问真实主题之前的预处理。");
}
public void PostRequest()
{
Console.WriteLine("访问真实主题之后的后续处理。");
}
}

程序运行的结果如下:

1
2
3
访问真实主题之前的预处理。
访问真实主题方法...
访问真实主题之后的后续处理。

前面分析了代理模式的结构与特点,现在来分析以下的应用场景:

  • 远程代理(Remote Proxy):这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。
    例如,用户访问网盘的虚拟硬盘时实际访问的是网盘空间。
  • 虚拟代理(Virtual Proxy):这种方式通常用于要创建的目标对象开销很大时。
    例如,下载一幅很大的图像需要很长时间,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
  • 保护代理(Protection Proxy):这种方式通常用于控制不同种类客户对真实对象的访问权限。
  • 智能指引(Smart Reference):主要用于调用目标对象时,代理附加一些额外的处理功能。

智能指引的典型用途包括:

  • 增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它;
  • 当第一次引用一个持久对象时,将它装入内存。
  • 在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。

在前面介绍的代理模式中,代理类中包含了对真实主题的引用,这种方式存在两个缺点:

  • 真实主题与代理主题一一对应,增加真实主题也要增加代理
  • 设计代理以前真实主题必须事先存在,不太灵活。

采用动态代理模式可以解决以上问题(如 SpringAOP),C#中可以使用RealProxy实现动态代理,有两种方法:
第一种:只使用RealProxy,不能代理带out参数的方法(可能是我没找到),代码如下:

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
class Program
{
static void Main(string[] args)
{
Console.WriteLine("***\r\n Begin program - logging with decorator\r\n");
IRepository<Customer> customerRepository =RepositoryFactory.Create<Customer>();
var customer = new Customer()
{
Id = 1,
Name = "Customer 1",
Address = "Address 1"
};
customerRepository.Add(customer);
customerRepository.Update(customer);
customerRepository.Delete(customer);
Console.WriteLine("\r\nEnd program - logging with decorator\r\n***");
Console.ReadLine();
}
}

public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}


public interface IRepository<T>
{
void Add(T entity);
void Delete(T entity);
void Update(T entity);
IEnumerable<T> GetAll();
T GetById(int id);
}


public class Repository<T> : IRepository<T>
{
public void Add(T entity)
{
Console.WriteLine("Adding {0}", entity);
}
public void Delete(T entity)
{
Console.WriteLine("Deleting {0}", entity);
}
public void Update(T entity)
{
Console.WriteLine("Updating {0}", entity);
}
public IEnumerable<T> GetAll()
{
Console.WriteLine("Getting entities");
return null;
}
public T GetById(int id)
{
Console.WriteLine("Getting entity {0}", id);
return default(T);
}
}


class DynamicProxy<T> : RealProxy
{
private readonly T _decorated;
public DynamicProxy(T decorated) : base(typeof(T))
{
_decorated = decorated;
}
private void Log(string msg, object arg = null)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(msg, arg);
Console.ResetColor();
}
public override IMessage Invoke(IMessage msg)
{
var methodCall = msg as IMethodCallMessage;
var methodInfo = methodCall.MethodBase as MethodInfo;
Log("In Dynamic Proxy - Before executing '{0}'",methodCall.MethodName);
try
{
var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
Log("In Dynamic Proxy - After executing '{0}' ",methodCall.MethodName);
return new ReturnMessage(result, null, 0,methodCall.LogicalCallContext, methodCall);
}
catch (Exception e)
{
Log(string.Format("In Dynamic Proxy- Exception {0} executing '{1}'", e),methodCall.MethodName);
return new ReturnMessage(e, methodCall);
}
}
}


public class RepositoryFactory
{
public static IRepository<T> Create<T>()
{
var repository = new Repository<T>();
var dynamicProxy = new DynamicProxy<IRepository<T>>(repository);
return dynamicProxy.GetTransparentProxy() as IRepository<T>;
}
}

第二种:使用RealProxy、MarshalByRefObject,可以代理带out参数的方法,代码如下:

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

public class Program
{
static void Main(string[] args)
{

Proxy<ISubject> proxy = new Proxy<ISubject>(new RealSubject());
ISubject subject = (ISubject)proxy.GetTransparentProxy();
int arg = 0;
subject.Request(out arg);
Console.WriteLine(arg);
Console.ReadKey();
}
}

public class Proxy<T> : RealProxy where T: class
{
MarshalByRefObject myMarshalByRefObject;
public Proxy(MarshalByRefObject realT) : base(typeof(T))
{
myMarshalByRefObject = realT;
}
public override IMessage Invoke(IMessage myMessage)
{
IMethodCallMessage myCallMessage = (IMethodCallMessage)myMessage;
Console.WriteLine("动态代理方法中:执行前");
IMethodReturnMessage myIMethodReturnMessage = RemotingServices.ExecuteMessage(myMarshalByRefObject, myCallMessage);
Console.WriteLine("动态代理方法中:执行后");
return myIMethodReturnMessage;
}
}


public interface ISubject
{
void Request(out int arg);
}


public class RealSubject : MarshalByRefObject,ISubject
{
public void Request(out int arg)
{
arg = 1;
Console.WriteLine("访问真实主题方法...");
}
}

C#中动态代理与泛型函数——CSDN
面向方面的编程-使用 RealProxy 类进行面向方面的编程——MSDN

在现实生活中,常常存在办事较复杂的例子,如办房产证或注册一家公司,有时要同多个部门联系,这时要是有一个综合部门能解决一切手续问题就好了。

软件设计也是这样,当一个系统的功能越来越强,子系统会越来越多,客户对系统的访问也变得越来越复杂。这时如果系统内部发生改变,客户端也要跟着改变,这违背了“开闭原则”,也违背了“迪米特法则”,所以有必要为多个子系统提供一个统一的接口,从而降低系统的耦合度,这就是外观模式的目标。

客户去当地房产局办理房产证过户要遇到的相关部门:

外观(Facade)模式的定义:是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。

外观(Facade)模式是“迪米特法则”的典型应用,它有以下主要优点:

  • 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
  • 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
  • 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。

外观(Facade)模式的主要缺点如下:

  • 不能很好地限制客户使用子系统类。
  • 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

外观(Facade)模式的结构比较简单,主要是定义了一个高层接口。它包含了对各个子系统的引用,客户端可以通过它访问各个子系统的功能。

模式的结构#

外观(Facade)模式包含以下主要角色:

  • 外观(Facade)角色:为多个子系统对外提供一个共同的接口。
  • 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
  • 客户(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
class Program
{
static void Main(string[] args)
{
Facade f = new Facade();
f.method();
Console.ReadLine();
}
}


public class Facade
{
private SubSystem01 obj1=new SubSystem01();
private SubSystem02 obj2=new SubSystem02();
private SubSystem03 obj3=new SubSystem03();
public void method()
{
obj1.Method1();
obj2.Method2();
obj3.Method3();
}
}

public class SubSystem01
{
public void Method1()
{
Console.WriteLine("子系统01的Method1()被调用!");
}
}


public class SubSystem02
{
public void Method2()
{
Console.WriteLine("子系统02的Method2()被调用!");
}
}


public class SubSystem03
{
public void Method3()
{
Console.WriteLine("子系统03的Method3()被调用!");
}
}

程序运行结果如下:

1
2
3
子系统01的Method1()被调用!
子系统02的Method2()被调用!
子系统03的Method3()被调用!

通常在以下情况下可以考虑使用外观模式:

  • 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
  • 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
  • 当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。

在外观模式中,当增加或移除子系统时需要修改外观类,这违背了“开闭原则”。如果引入抽象外观类(或接口),则在一定程度上解决了该问题,其结构图如图所示:

在现实生活中,存在很多“部分-整体”的关系,例如,大学中的部门与学院、总公司中的部门与分公司、学习用品中的书与书包、生活用品中的衣月艮与衣柜以及厨房中的锅碗瓢盆等。
在软件开发中也是这样,例如,文件系统中的文件与文件夹、窗体程序中的简单控件与容器控件等。对这些简单对象与复合对象的处理,如果用组合模式来实现会很方便。

组合(Composite)模式的定义:有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。

组合模式的主要优点有:

  • 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码
  • 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”

其主要缺点是:

  • 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
  • 不容易限制容器中的构件;
  • 不容易用继承的方法来增加构件的新功能;

模式的结构#

组合模式包含以下主要角色:

  • 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。
  • 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。
  • 树枝构件(Composite)角色:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法

组合模式分为透明式的组合模式和安全式的组合模式

  • 透明方式:在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。其结构图如图所示:

  • 安全方式:在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。其结构图如图所示:

模式的实现#

假如要访问集合 c0={leaf1,{leaf2,leaf3}} 中的元素,其对应的树状图如图所示:

下面给出透明式的组合模式的实现代码,安全式的组合模式与之类似:

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 Program
{
static void Main(string[] args)
{
IComponent c0=new Composite();
IComponent c1=new Composite();
IComponent leaf1=new Leaf("1");
IComponent leaf2=new Leaf("2");
IComponent leaf3=new Leaf("3");
c0.Add(leaf1);
c0.Add(c1);
c1.Add(leaf2);
c1.Add(leaf3);
c0.Operation();
Console.Read();
}
}


public interface IComponent
{
void Add(IComponent c);
void Remove(IComponent c);
IComponent GetChild(int i);
void Operation();
}


public class Leaf : IComponent
{
private String name;
public Leaf(String name)
{
this.name=name;
}
public void Add(IComponent c){ }
public void Remove(IComponent c){ }
public IComponent GetChild(int i)
{
return null;
}
public void Operation()
{
Console.WriteLine("树叶"+name+":被访问!");
}
}


public class Composite : IComponent
{
private List<IComponent> children=new List<IComponent>();
public void Add(IComponent c)
{
children.Add(c);
}
public void Remove(IComponent c)
{
children.Remove(c);
}
public IComponent GetChild(int i)
{
return children[i];
}
public void Operation()
{
foreach (var obj in children)
{
obj.Operation();
}
}
}

程序运行结果如下:

1
2
3
树叶1:被访问!
树叶2:被访问!
树叶3:被访问!

前面分析了组合模式的结构与特点,下面分析它适用的以下应用场景:

  • 在需要表示一个对象整体与部分的层次结构的场合。
  • 要求对用户隐藏组合对象与单个对象的不同,用户可以用统一的接口使用组合结构中的所有对象的场合。

如果对前面介绍的组合模式中的树叶节点和树枝节点进行抽象,也就是说树叶节点和树枝节点还有子节点,这时组合模式就扩展成复杂的组合模式了,如 Java AWT/Swing 中的简单组件 JTextComponent 有子类 JTextField、JTextArea,容器组件 Container 也有子类 Window、Panel。复杂的组合模式的结构图如图所示:

在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框等。
在软件开发过程中,有时想用一些现存的组件。这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。所有这些都可以釆用装饰模式来实现。

装饰(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式

装饰(Decorator)模式的主要优点有:

  • 采用装饰模式扩展对象的功能比采用继承方式更加灵活
  • 可以设计出多个不同的具体装饰类,创造出多个不同行为的组合

其主要缺点是:装饰模式增加了许多子类,如果过度使用会使程序变得很复杂

通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰模式的目标。

模式的结构

装饰模式主要包含以下角色:

  • 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
  • 具体构件(Concrete Component)角色:实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

装饰模式的结构图如图所示:

模式的实现#

装饰模式的实现代码如下:

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
class Program
{
static void Main(string[] args)
{
IComponent p=new ConcreteComponent();
p.Operation();
Console.WriteLine("---------------------------------");
IComponent d=new ConcreteDecoratorA(p);
d.Operation();
Console.ReadLine();
}
}


public interface IComponent
{
void Operation();
}


public class ConcreteComponent : IComponent
{
public ConcreteComponent()
{
Console.WriteLine("创建具体构件角色");
}
public void Operation()
{
Console.WriteLine("调用具体构件角色的方法Operation()");
}
}


public abstract class Decorator : IComponent
{
private IComponent component;
public Decorator(IComponent component)
{
this.component=component;
}
public virtual void Operation()
{
component.Operation();
}
}


public class ConcreteDecoratorA : Decorator
{
public ConcreteDecoratorA(IComponent component)
: base(component)
{

}
public override void Operation()
{
base.Operation();
AddedFunction();
}
public void AddedFunction()
{
Console.WriteLine("为具体构件角色增加额外的功能AddedFunction()");
}
}

程序运行结果如下:

1
2
3
4
5
创建具体构件角色
调用具体构件角色的方法Operation()
---------------------------------
调用具体构件角色的方法Operation()
为具体构件角色增加额外的功能AddedFunction()

装饰模式通常在以下几种情况使用:

  • 当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。
  • 当需要通过对现有的一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却很好实现。
  • 当对象的功能要求可以动态地添加,也可以再动态地撤销时。

Java的java.io包和.NET的Stream类都使用了该模式。我不会就它们的实现谈论很多细节。.NET中,Stream是一个抽象类,提供了基本的行为(如Component),从抽象类Stream继承来的类FileStream,MemoryStream是ConcreteCompents,类BufferedStream,CryptoStream等是ConcreteDecorators。你能清楚地认识到它们同样忽略了Decorator

装饰模式所包含的 4 个角色不是任何时候都要存在的,在有些应用环境下模式是可以简化的,如以下两种情况:

  • 如果只有一个具体构件而没有抽象构件时,可以让抽象装饰继承具体构件,其结构图如图所示:

  • 如果只有一个具体装饰时,可以将抽象装饰和具体装饰合并,其结构图如图所示: