目录
装饰模式
引言
装饰模式是一种用于替代继承的技术,它通过一种无需定义子类的方式来给对象动态地增加职责,使用对象之间的关联关系取代类之间的继承关系。
定义
英文定义:"Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality."。
中文定义:动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。
享元模式重要等级★★★☆☆ 享元模式难度等级★★★☆☆
模式类图
装饰模式包含以下角色:
1.Componnet 抽象构件
2.ConcreteComponent 具体构件
3. Decorator 抽象装饰类:抽象装饰类是抽象构件的子类,并且维护一个指向抽象构件的引用,通过改引用可以调用装饰之前的对象方法。并通过扩展改方法,以达到装饰的目的。
4.ConcreteDecorator 具体装饰类
实例
实例描述
在某图书管理系统中,书籍类(Book)具有借书方法(BorrowBook)和还书方法(ReturnBook),现需要动态给书籍对象添加冻结方法(Freeze)和遗失方法(Lose)。使用装饰模式设计该系统。
实例类图
代码实现
Book类,定义为接口
interface Book
{
void BorrowBook(string bookName);
void ReturnBook(string bookName);
}
普通图书类SimpleBook
class SimpleBook : Book
{
public void BorrowBook(string bookName)
{
Console.WriteLine("借阅图书<<{0}>>", bookName);
}
public void ReturnBook(string bookName)
{
Console.WriteLine("归还图书<<{0}>>", bookName);
}
}
抽象装饰类,定义为抽象类,实现Book接口
abstract class BookDecorator : Book
{
protected Book book;
public BookDecorator(Book book)
{
this.book = book;
}
public virtual void BorrowBook(string bookName)
{
book.BorrowBook(bookName);
}
public virtual void ReturnBook(string bookName)
{
book.ReturnBook(bookName);
}
}
具体装饰类,丢失图书LoseBook
class LoseBook : BookDecorator
{
public LoseBook(Book book) : base(book) { }
public void Lose(string bookName)
{
Console.WriteLine("<<{0}>>已经遗失!",bookName);
}
}
具体装饰类,冻结图书FreezeBook
class FreezeBook : BookDecorator
{
public FreezeBook(Book book) : base(book) { }
public void Freeze(string bookName)
{
Console.WriteLine("《{0}》已经被冻结!",bookName);
}
}
测试代码
class Program
{
static void Main(string[] args)
{
SimpleBook b1;
b1 = new SimpleBook();
b1.BorrowBook("西游记");
b1.ReturnBook("西游记");
FreezeBook b2 = new FreezeBook(b1);
b2.BorrowBook("西游记");
b2.ReturnBook("西游记");
b2.Freeze("西游记");
LoseBook b3 = new LoseBook(b1);
b3.BorrowBook("西游记");
b3.ReturnBook("西游记");
b3.Lose("西游记");
Console.ReadKey();
}
}
运行结果
模式扩展
装饰模式的简化
如果只有一个具体构件类,抽象装饰类可以作为具体构建类的直接子类,装饰模式简化,如下图所示。
透明装饰模式和半透明装饰模式
透明指的是客户端是否完全针对抽象编程,如前文的实例中,测试代码声明为具体构件,因为具体装饰类添加了新的功能。是半透明的装饰模式。
透明装饰模式要求客户端完全针对抽象编程,这意味着新的具体装饰类是对原有功能的补充,而不能添加新的,在抽象层没有定义的功能。下面用一个多重加密的实例来演示透明装饰模式。
实例
实例描述
编写一个字符串加密类,用装饰模式实现可以动态地多重加密。
实例类图
代码实现
抽象加密类Cipher
interface Cipher
{
string Encrypt(string text);
}
简单加密类SimpleCipher
class SimpleCipher : Cipher
{
public string Encrypt(string text)
{
throw new NotImplementedException();
}
}
抽象装饰加密类CipherDecorator
abstract class CipherDecorator : Cipher
{
protected Cipher cipher;
public CipherDecorator(Cipher cipher)
{
this.cipher = cipher;
}
public abstract string Encrypt(string text);
}
复杂加密类ComplexCipher
class ComplexCipher : CipherDecorator
{
public ComplexCipher(Cipher c) : base(c) { }
public override string Encrypt(string text)
{
return ComplexEncrypt(cipher.Encrypt(text));
}
private string ComplexEncrypt(string text)
{
throw new NotImplementedException();
}
}
高级加密类AdvancedCipher
class AdvanceCipher : CipherDecorator
{
public AdvanceCipher(Cipher c) : base(c) { }
public override string Encrypt(string text)
{
return AdvanceEncrypt(cipher.Encrypt(text));
}
private string AdvanceEncrypt(string text)
{
throw new NotImplementedException();
}
}
测试代码
class Program
{
static void Main(string[] args)
{
string password = "asd123";
//初次加密
Cipher c = new SimpleCipher();
password = c.Encrypt(password);
//使用复杂加密再加密
c = new ComplexCipher(c);
password = c.Encrypt(password);
//使用高级加密再加密
c = new AdvanceCipher(c);
password = c.Encrypt(password);
Console.WriteLine(password);
Console.ReadKey();
}
}
运行结果
由于本例中的加密算法实际上未实现,所以不贴出运行结果。读者可以根据自己喜好实现自己想要的加密算法,将代码中的throw new NotImplementedException();换成具体的加密算法,使用不同的排列组合就可以达到多次加密的效果!
总结
模式优点
1.用装饰模式扩展对象的功能,比用继承的方式更加灵活。
2.可以通过一种动态的方式扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器没从而实现不同的行为。
3.通过使用不同的多个具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。(如加密可以多重加密)
4.具体构件类和具体装饰类可以独立变化,用户可以根据需要增加新的具体构件和装饰类,在使用的时候再进行组合,原有代码无需改变,复合开闭原则。
模式缺点
1.使用装饰模式进行系统设计的时候,会产生很多小对象,这些对象的区别在于他们之间相互连接的方式不同,而不是它们的类或者属性有所不同,同时还会产生很多具体装饰类。这将加大系统的复杂度。
2.对于多次装饰的对象,调试时寻找错误需要一层层的排查,较为繁琐。