意图
不使用创建子类的方法动态的增加类的功能。
结构
模式的参与者如下:
Compeonent:定义一个对象接口,可以动态添加这些对象的功能。
ConcreteComponent:定义一个对象,可以动态的为其添加一些功能。
Decorator:维持一个对Component对象的引用,并定义与Component接口的一致接口。
ConcreteDecorator:为组件添加功能。
下面举的是一个为数据访问类添加日志功能的例子。
数据访问类包括支持SQLServer、Oracle和OLE三种类型的数据库访问的类。现在需要在数据库访问类的基础上增加记录数据库操作的日志功能。日志的记录方式可以是关系数据库或文件形式。
数据库访问类的抽象类:定义一个对象接口,可以动态添加这些对象的功能。
using System.Collections.Generic;
using System.Text;
using System.Data;
namespace DecoratorPattern.ex3
{
public abstract class clsAbstractDB
{
private string _connString;
public string ConnString
{
get { return _connString; }
set { _connString = value; }
}
public clsAbstractDB()
{
_connString = "";
}
public clsAbstractDB(string connstr)
{
_connString = connstr;
}
public abstract void Open();
public abstract void Close();
public abstract DataSet ExecSQL(string strSQL);
public abstract void ExecSQLNonQuery(string strSQL);
}
}
SQLServer的数据库访问类
using System.Collections.Generic;
using System.Text;
using System.Data;
namespace DecoratorPattern.ex3
{
public class clsSQLServerDB:clsAbstractDB
{
public override void Open()
{
Console.WriteLine("clsSQLServerDB.Open");
}
public override void Close()
{
Console.WriteLine("clsSQLServerDB.Close");
}
public override System.Data.DataSet ExecSQL(string strSQL)
{
DataSet ds = new DataSet();
Console.WriteLine("clsSQLServerDB.ExecSQL:" + strSQL);
return ds;
}
public override void ExecSQLNonQuery(string strSQL)
{
Console.WriteLine("clsSQLServerDB.ExecSQLNonQuery" + strSQL);
}
}
}
Oracle的数据库访问类
using System.Collections.Generic;
using System.Text;
using System.Data;
namespace DecoratorPattern.ex3
{
public class clsOracleDB : clsAbstractDB
{
public override void Open()
{
Console.WriteLine("clsOracleDB.Open");
}
public override void Close()
{
Console.WriteLine("clsOracleDB.Close");
}
public override System.Data.DataSet ExecSQL(string strSQL)
{
DataSet ds = new DataSet();
Console.WriteLine("clsOracleDB.ExecSQL:" + strSQL);
return ds;
}
public override void ExecSQLNonQuery(string strSQL)
{
Console.WriteLine("clsOracleDB.ExecSQLNonQuery" + strSQL);
}
}
}
OLE的数据库访问类
using System.Collections.Generic;
using System.Text;
using System.Data;
namespace DecoratorPattern.ex3
{
public class clsOLEDB : clsAbstractDB
{
public override void Open()
{
Console.WriteLine("clsOLEDB.Open");
}
public override void Close()
{
Console.WriteLine("clsOLEDB.Close");
}
public override System.Data.DataSet ExecSQL(string strSQL)
{
DataSet ds = new DataSet();
Console.WriteLine("clsOLEDB.ExecSQL:" + strSQL);
return ds;
}
public override void ExecSQLNonQuery(string strSQL)
{
Console.WriteLine("clsOLEDB.ExecSQLNonQuery" + strSQL);
}
}
}
实现功能扩展的接口。
using System.Collections.Generic;
using System.Text;
using System.Data;
namespace DecoratorPattern.ex3
{
public abstract class Log : clsAbstractDB
{
private clsAbstractDB _myDB;
public clsAbstractDB DB
{
get { return _myDB;}
set { _myDB=value;}
}
public Log(clsAbstractDB mydb)
{
this._myDB = mydb;
}
public override void Open()
{
_myDB.Open();
WriteLog(GetmyDBType() + " Open");
}
public override void Close()
{
_myDB.Close();
WriteLog(GetmyDBType() + " Close");
}
public override DataSet ExecSQL(string strSQL)
{
DataSet ds=_myDB.ExecSQL(strSQL);
WriteLog(GetmyDBType() + " ExecSQL");
return ds;
}
public override void ExecSQLNonQuery(string strSQL)
{
_myDB.ExecSQLNonQuery(strSQL);
WriteLog(GetmyDBType() + " ExecSQLNonQuery");
}
public abstract void WriteLog(string str);
private string GetmyDBType()
{
Type t=_myDB.GetType();
return t.FullName;
}
}
}
支持关系数据库的日志功能的类。
using System.Collections.Generic;
using System.Text;
using System.Data;
namespace DecoratorPattern.ex3
{
public class LogDB : Log
{
public LogDB(clsAbstractDB mydb):base(mydb)
{
base.DB = mydb;
}
public override void Open()
{
base.Open();
}
public override void Close()
{
base.Open();
}
public override System.Data.DataSet ExecSQL(string strSQL)
{
return base.ExecSQL(strSQL);
}
public override void ExecSQLNonQuery(string strSQL)
{
base.ExecSQLNonQuery(strSQL);
}
public override void WriteLog(string str)
{
Console.WriteLine("LogDB.WriteLog:" + str);
Console.WriteLine("");
}
}
}
支持XML文件的日志功能的类。
using System.Collections.Generic;
using System.Text;
namespace DecoratorPattern.ex3
{
public class LogXml : Log
{
public LogXml(clsAbstractDB mydb)
: base(mydb)
{
base.DB = mydb;
}
public override void Open()
{
base.Open();
}
public override void Close()
{
base.Open();
}
public override System.Data.DataSet ExecSQL(string strSQL)
{
return base.ExecSQL(strSQL);
}
public override void ExecSQLNonQuery(string strSQL)
{
base.ExecSQLNonQuery(strSQL);
}
public override void WriteLog(string str)
{
Console.WriteLine("LogXml.WriteLog:" + str);
Console.WriteLine("");
}
}
}
获得具体类型的工厂
using System.Configuration;
using System.Collections.Generic;
using System.Text;
namespace DecoratorPattern.ex3
{
public class clsCreateDB
{
public static clsAbstractDB CreateDB()
{
clsAbstractDB clsDB;
string strType = ConfigurationManager.AppSettings["strDBType"];
string strConn = ConfigurationManager.AppSettings["strConn"];
switch(strType)
{
case "clsSQLServerDB":
clsDB = new clsSQLServerDB(strConn);
break;
case "clsOLEDB":
clsDB = new clsOLEDB(strConn);
break;
case "clsOracleDB":
clsDB = new clsOracleDB(strConn);
break;
default:
clsDB = null;
break;
}
return clsDB;
}
public static Log CreateLog(clsAbstractDB DB)
{
Log log;
string strLogType = ConfigurationManager.AppSettings["strLogType"];
switch (strLogType)
{
case "LogDB":
log = new LogDB(DB);
break;
case "LogXml":
log = new LogXml(DB);
break;
default:
log = null;
break;
}
return log;
}
}
}
使用的代码
ex3.clsAbstractDB db = ex3.clsCreateDB.CreateDB();
//实现日志功能的扩展
ex3.Log log = ex3.clsCreateDB.CreateLog(db);
log.Open();
DataSet ds=log.ExecSQL("select * from customers");
log.ExecSQLNonQuery("select * from customers");
log.Close();
}
适用性
(1)在不影响其它对象的情况下,以动态且透明的方式添加单个对象的功能。
(2)处理那些可以撤销的功能。
(3)不能生成子类的方法扩充时。
效果
Decorator模式的优点是提供了比继承更加灵活的扩展,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。
由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。
BufferStream——.NET中的装饰模式
.NET中Decorator模式一个典型的运用就是关于Stream,它存在着如下的类结构:
可以看到, BufferedStream和CryptoStream其实就是两个包装类,这里的Decorator模式省略了抽象装饰角色(Decorator),示例代码如下:
{
public static void Main(string[] args)
{
MemoryStream ms =
new MemoryStream(new byte[] { 100,456,864,222,567});
//扩展了缓冲的功能
BufferedStream buff = new BufferedStream(ms);
//扩展了缓冲,加密的功能
CryptoStream crypto = new CryptoStream(buff);
}
}
改变外观还是改变内核
从适应对象变化的角度看,有两种方法可以达到目的,一种方法是制作一个小粒度的内核对象,然后进行包装,这是装饰模式实现的方法;另一种方法是制作一个大粒度的对象,其中包括不可变的部分和可变的部分,可变的部分可以采用策略模式实现。