.Net中之设计模式——Decorator模式

by admin on 2018年11月15日

讲解.Net Framework中的Decorator模式。

   
设想这样一个要求,我们要呢和谐之框架提供一个当排序的组件。目前要贯彻之是冒泡排序算法和高速排序算法,根据“面向接口编程”的思想,我们可以吗这些排序算法提供一个联结之接口ISort,在这接口中来一个法Sort(),它能承受一个object数组参数。对数组进行排序后,返回该数组。接口的概念如下:

请访问:.Net中的设计模式——Decorator模式

       public interface ISort
       {
              void Sort(ref object[] beSorted);
       }

一律、模式概述

其类图如下:

一个面貌是咱若吧一个靶动态增长新的职责,这个任务并无改动原有的行,而是以原本行为基础及添加新的效益,就吓于装饰工人为同栋新居的墙上涂去上五彩缤纷的颜色一般。
自咱有着的面向对象的文化出发,为一个对象多新的天职,完全好运用连续机制,然而更经实例化派生的子类,来抱新增的职责。由于要以原行为基础及补偿加新职能,此时父类的章程应该吗虚方法,例如用户登录行为:
public class User
{
 public virtual void SignIn()
 {
  Console.WriteLine(“The User Sign In.”);
 }
}
假设急需吗用户登录行为增多权限验证的天职,可以定义一个子类继承User类:
public class SecurityUser:User
{
 public override void SignIn()
 {
  if (IsValid())
  {
   base.SignIn();
  }
  else
  {
   throw new NotAuthorizationException();
  }
 }
 private bool IsValid()
 {
  //略;
  return true;
 }
}
兑现的类图如下: 

图片 1

图片 2

只是一般对于排序而言,排列是产生各个的分的,例如升序,或者降序,返回的结果吧非雷同。最简便的主意我们可下if语句来实现这无异于目的,例如在QuickSort类中:

不过连续机制的一个受制是它们不得不静态地加上对象职责,一旦增长的任务来转换,例如客户需要呢报到行为添加日志记录功能,虽然咱得另行定义一个子类,重写SignIn()方法,然而我们倒是休克控制任务添加的机遇和办法。此外,当User具有自己的连续体系时,则该并无克用SecurityUser的任务。例如,User同时具备另外一个子类Employee,则Employee类的报到行为还是沿用了那个父类User的记名行为,而非备权限验证的职责。如果需要要求Employee同样如果验证登录权限,就得重新为它们创建一个子类SecurityEmployee,如图: 

public class QuickSort:ISort

图片 3

{

这般的结果会招类似的数据会趁着需求的变迁而无限量的增多,同时关于权限验证的任务也从不能够获得充分的选用,这明明违反了面向对象设计的旺盛。
还有一个抬高职责的计,就是以做,将旧的对象放置到一个新的近乎中,由此来好对新职责的长,例如:
public class SecurityUser
{
 private User m_user;
 public User User
 {
  get {return m_user;}
  set {m_user = value;}
 }
 public void SignIn()
 {
  if (IsValid())
  {
   m_user.SignIn();
  }
else
  {
   throw new NotAuthorizationException();
  }

       private
string m_SortType;

 }
 private bool IsValid()
 {
  //略;
  return true;
 }
}
实现之类图如下:

       public
QuickSort(string sortType)

图片 4

       {

较打继续机制而言,组合措施更加的利落,尤其当面对User具有本身的继续体系时,如齐之设计无需另外修改,就会从容的应: 

             
m_SortType = sortType;

图片 5

       }

这时咱们就待用Employee对象与给SecurityUser中的User属性就足以兑现对Employee权限验证的报到。因此,我们避免了先用继承机制所面临的有数只问题:
1、 类的无限量增加(所谓的“类爆炸”);
2、 权限验证职责的不得重用。
只是结合也错过了继续的许多优势,其中,不拥有对象的多态特质,就是无比特别之一个受制。例如,如下创建对象的计尽管是不当的:
User user = new SecurityUser();
当一个道体现为对User的操作时,此时SecurityUser就无法对User类型的目标进行替代,这便限制了软件之不过扩展性。例如,在表示层逻辑中,会生一个报到页面将调用SignIn方法:
public class LoginPage
{
 public static void SignIn(User user);
}
于这种情况下,SecurityUser对象就是无法做也参数传入,那么,我们当前边所犯的职责添加的拼命岂不纵是泯灭了邪?
一经条分缕析考察继承与组合的上下,我们发现要用连续机制同构成措施两者结合起来,将会打及意想不到的效果,此时,各自的亮点弥补了分别的老毛病,完美地缓解了“为对象动态增长新的天职”的需求。
乍的缓解方案如下图所示: 

       public
void Sort(ref object[] beSorted)

图片 6

       {

实质上,如齐的类图结构即是设计模式中之Decorator模式,User是给点缀的目标,而SecurityUser则是Decorator,也即是咱们所谓的“装饰工”。在这里,SecurityUser是一个具体类,如果起新的装点需求,例如之前涉嫌的增日志记录功能,同样需树立装饰类。此时,对于现实的装饰类而言,具有局部同等之逻辑,在是前提下,可以吗那个长一个Decorator抽象类: 

             
if (m_SortType.ToUpper().Trim() == “ASCENDING”)

图片 7

             
{

这,我用本的SecurityUser类更名为SecuritySignInDecorator,便于理解。SignInDecorator是一个抽象类,继承了User类,同时User类对象又作一个属性在于SignInDecorator抽象类中。注意,这里的重组措施实际有多种落实方式。如作为一个特性,或者当做构造函数的参数等等。
泛泛类SignInDecorator的代码如下所示:
public abstract SignInDecorator:User
{
 private User m_user;
 public User User
 {
  get {return m_user;}
  set {m_user = value;}
 }
 public override void SignIn()
 {
  if (m_user != null)
  {
   m_user.SignIn();
  }
 }
}
倘SignInDecorator的子类定义则如下:
public class SecuritySignInDecorator:SignInDecorator
{
 public override void SignIn()
 {
  if (IsValid())
  {
   base.SignIn();
  }
  else
  {
   throw new NotAuthorizationException();
  }
 }
 private bool IsValid()
 {
  //略;
  return true;
 }
}
public class LoggingSignInDecorator:SignInDecorator
{
 public override void SignIn()
 {
  base.SignIn();
  Logging();  
 }
 private void Logging()
 {
  //略;
 }
}
即之结构完全解决了前头利用连续或组合所出现的题目,避免了看似的极致增加,权限验证或者日志记录的职责也能怪好地用,同时权限验证和日志记录等装饰类由于同样继承了User,因此根据多态原理,是好了替换User类型的靶子的。
除此以外,利用Decorator模式还得解决动态构成装饰的题目,例如为SignIn()方法既填补加权限验证功能,又补加日志记录功能,此时并不需要新增一个看似。实现如下:
User user = new User();
SignInDecorator securtiyDec = new SecuritySignInDecorator();
securityDec.User = user;
SignInDecorator loggingDec = new LoggingSignInDecorator();
loggingDec.User = securityDec;
loggingDec.SignIn();
loggingDec.SignIn()方法执行之时序图如下:

                    
//执行升序的神速排序;

图片 8

             
}

从今时先后图备受得望,当我们调用LoggingSignInDecorator对象的SignIn()方法时,因为LoggingSignInDecorator对象的User属性值为SecuritySignInDecorator对象,所以将执行SecuritySignInDecorator对象的SignIn()方法,该方法会先实行IsValid()私有方法,然而更调用User属性对象的SignIn()方法。由于SecuritySignInDecorator的User属性值为User对象,因此推行User对象的SignIn()方法。待SignIn()方法执行了,最后再次实施LoggingSignInDecorator对象的Logging()方法。
留神上述的时序图,如果以LoggingSignInDecorator对象的SignIn()方法吃,Logging()方法在base.SignIn()前,则应事先实施Logging()方法,然后才是SignIn()方法。
无需上加新的Decorator对象,通过上述的贯彻方式,我们虽即兴地形成了针对User对象SignIn()方法权限控制与日志记录之装修。 

             
else

二、.Net Framework中的Decorator模式

             
{

于.Net
Framework中,有关流的处理就使用了Decorator模式。我们知晓,所有的流操作都产生一个一头的基类System.IO.Stream,它是一个抽象类,主要包含了Read、Write等作为。而对文件流和网流定义的类FileStream和NetworkStream,都持续了Stream类,它们的读写操作的贯彻自是不同之。然而,当我们需要增强流读写性能的下,不管是文本流还是网络流,.Net都提供了相同的法子,即经过Buffer存放流数据为达性能改进的目的。此时,Buffer的作用对流动的读写操作而言,就相当给一个装修的来意。同样的,如果我们要求针对文件流或网络流的数据读写进行加密操作,以保数据的平安,那么这个加密的天职同样是如出一辙栽装饰作用,并且Buffer与加密任务是互相不悖的,有时候可能需要呢流操作共同长这简单起意义,这些要求完全符合Decorator模式。
在.Net Framework中,以上的实现好用类图来代表: 

                    
//执行降序的快捷排序;

图片 9

             
}

于类图中,BufferedStream和CryptoStream就相当给Decorator类,不过当此处连不曾概念抽象的Decorator类,因为它不需要发联手之逻辑进行抽象。
.Net Framework对于Stream类的定义如下:
public abstract class Stream:MashalByRefObject,IDisposable
{
 static Stream()
 {
  Stream.Null = new Stream.NullStream();
 }
 protected Stream()
 {
  this._asyncActiveCount = 1;
 }
 public abstract int Read([In,Out]byte[] buffer, int offset, int
count);
 public abstract void Write(byte[] buffer, int offset, int count);
 //……
 [NonSerialized]
 private int _asyncActiveCount;
}
留神在BufferedStream和CryptoStream类中凡于构造函数中传出要装修的对象:
public sealed class BufferedStream:Stream
{
 public BufferedStream(Stream stream):this(stream,0x1000){}
 public BufferedStream(Stream stream, int bufferSize)
{
      if (stream == null)
      {
            throw new ArgumentNullException(“stream”);
      }
      if (bufferSize <= 0)
      {
            throw new ArgumentOutOfRangeException(“bufferSize”,
string.Format(CultureInfo.CurrentCulture,
Environment.GetResourceString(“ArgumentOutOfRange_MustBePositive”), new
object[] { “bufferSize” }));
      }
      this._s = stream;
      this._bufferSize = bufferSize;
      if (!this._s.CanRead && !this._s.CanWrite)
      {
            __Error.StreamIsClosed();
      }
}
public override int Read([In, Out] byte[] array, int offset, int
count)
{
      //……
      if (this._s == null)
      {
            __Error.StreamIsClosed();
      }
      int num1 = this._readLen – this._readPos;
      if (num1 == 0)
      {
            //处理buffer的操作
            num1 = this._s.Read(this._buffer, 0, this._bufferSize);
            if (num1 == 0)
            {
                  return 0;
            }
            this._readPos = 0;
            this._readLen = num1;
      }
      //……
      Buffer.InternalBlockCopy(this._buffer, this._readPos, array,
offset, num1);
      this._readPos += num1;
      if (num1 < count)
      {
            int num2 = this._s.Read(array, offset + num1, count –
num1);
            num1 += num2;
            this._readPos = 0;
            this._readLen = 0;
      }
      return num1;
}
//Write同另措施有些
private Stream _s;
}
BufferedStream类中private字段_s为Stream类型,在Read()方法吃,处理了Buffer的操作后,就将调用_s的Read()方法,以此实现对Stream对象Read行为的装修效果。
CryptoStream类的实现同BufferedStream类似,都是连续了纸上谈兵类Stream,同时通过构造函数的参数引入一个Stream对象。
在运BufferedStream和CryptoStream时,可以将FileStream或NetworkStream等Stream子类对象通过其构造函数传入,以达到装饰的目的。由于其本身也延续了Stream类,因此它们相互之间吧堪装点,例如下面的代码:
public static void EncryptTextToFile(String Data, String FileName,
byte[] Key, byte[] IV)
{
    try
    {
        FileStream fStream = File.Open(FileName,
FileMode.OpenOrCreate); 

       }    

        // Create a new Rijndael object.
        Rijndael RijndaelAlg = Rijndael.Create();

}

        // Create a CryptoStream using the FileStream
        // and the passed key and initialization vector (IV).
        CryptoStream cStream = new CryptoStream(fStream,
        RijndaelAlg.CreateEncryptor(Key, IV),
        CryptoStreamMode.Write);
    // Create a StreamWriter using the CryptoStream.
        StreamWriter sWriter = new StreamWriter(cStream);
    try
    {
        // Write the data to the stream to encrypt it.
        sWriter.WriteLine(Data);
    }
    catch (Exception e)
    {
        Console.WriteLine(“An error occurred: {0}”, e.Message);
    }
    finally
    {       
        sWriter.Close();
        cStream.Close();
        fStream.Close();
    }
 }
 catch (CryptographicException e)
 {
     Console.WriteLine(“A Cryptographic error occurred: {0}”,
e.Message);
 }
 catch (UnauthorizedAccessException e)
 {
     Console.WriteLine(“A file error occurred: {0}”, e.Message);
 }
}
每当这里,就是将FileStream对象通过如下代码组合以CryptoStream对象中:
CryptoStream cStream = new CryptoStream(fStream,
        RijndaelAlg.CreateEncryptor(Key, IV),
        CryptoStreamMode.Write);
接下来StreamWriter再运该CryptoStream对象开展摹写操作时,写到文件FileName中之,就是加密后的多寡。也就是说,我们动态地也FileStream添加了加密的功能。

自然,我们也得用string类型的SortType定义为枚举类型,减少出现谬误的可能。然而细心阅读代码,我们得发现这样的代码是可怜僵化的,一旦得扩大,如果要求我们增加新的排序依次,例如字典顺序,那么我们面临的干活会晤非常艰巨。也就是说,变化来了。通过分析,我们发现所谓排序的依次,恰恰是排序算法中极要紧之均等环抱,它决定了哪个排在前头,谁排于晚。然而她并无属于排序算法,而是同种于的国策,后者即比较的行。

如若条分缕析分析实现ISort接口的接近,例如QuickSort类,它于贯彻排序算法的早晚,需要针对有限独对象作于。按照重构的做法,实质上我们得以在Sort方法吃抽取产生一个私房方法Compare(),通过返回的布尔值,决定谁目标在前头,哪个目标在继。显然,可能发生变化的凡者于行为,利用“封装抽象”的规律,就应有吗该作为确立一个专有的接口ICompare,然而分别定义实现升序、降序或者字典排序的类似对象。

图片 10

咱们在各个一个实现了ISort接口的近乎构造函数中,引入ICompare接口对象,从而建立起排序算法和于算法的弱耦合关系(因为是涉及和虚空的ICompare接口相关),例如QuickSort类:

public class QuickSort:ISort

{

       private
ICompare m_Compare;

       public
QuickSort(ICompare compare)

       {

             
m_Compare= compare;

       }

       public
void Sort(ref object[] beSorted)

       {

             
//实现略

             
for (int i = 0; i < beSorted.Length – 1; i++)

            
{

                    
if (m_Compare.Compare(beSorted[i],beSorted[i+1))

                    
{

                           
//略;

                    
}

             
}

             
//实现略

       }    
      
}

末了之类图如下:

图片 11

由此对比较策略的卷入,以承诺针对它的变,显然是Stategy模式的设计。事实上,这里的排序算法为说不定是生成之,例如落实二叉树排序。由于我们曾经引入了“面向接口编程”的考虑,我们全然可以自由的丰富一个初的类BinaryTreeSort,来贯彻ISort接口。对于调用方而言,ISort接口的实现,同样是一个Strategy模式。此时之类组织,完全是一个针对性扩大开发之状态,它完全能够适应类库调用者新需求的生成。

再度因为PetShop为例,在是路被涉及到订单的管制,例如插入订单。考虑到访问量的干,PetShop为订单管理提供了齐和异步的计。显然,在实际使用被只能采用这片种植方式的里边同样种,并由现实的应用环境所控制。那么为回应这样同样种植或会见充分频繁之成形,我们照样要使用“封装变化”的原理,建立抽象级别之对象,也尽管是IOrderStrategy接口:

public interface IOrderStrategy

{

       void
Insert(PetShop.Model.OrderInfo order);

}

然后定义两单类OrderSynchronous和OrderAsynchronous。类组织如下:

图片 12

每当PetShop中,由于用户时时都或会见变动插入订单的策略,因此于业务层的订单领域对象而言,不克跟具象的订单策略对象有耦合关系。也就是说,在天地对象Order类中,不能够new一个有血有肉的订单策略对象,如下面的代码:

IOrderStrategy orderInsertStrategy = new OrderSynchronous();

于Martin Fowler的稿子《IoC容器和Dependency
Injection模式》中,提出了化解当时好像问题之法,他称之为依赖注入。不过是因为PetShop并没用如Sping.Net等IoC容器,因此解决因问题,通常是使配置文件结合反射来好的。在世界对象Order类中,是这般实现的:

public class Order

{

       private
static readonly IOrderStategy orderInsertStrategy =
LoadInsertStrategy();

       private
static IOrderStrategy LoadInsertStrategy()

{

           // Look up which strategy to use
from config file

           string path =
ConfigurationManager.AppSettings[“OrderStrategyAssembly”];

           string className =
ConfigurationManager.AppSettings[“OrderStrategyClass”];

 

           // Load the appropriate assembly
and class

           return
(IOrderStrategy)Assembly.Load(path).CreateInstance(className);

}

}

于布局文件web.config中,配置如下的Section:

<add key=”OrderStrategyAssembly” value=”PetShop.BLL”/>
<add key=”OrderStrategyClass”
value=”PetShop.BLL.OrderSynchronous”/>

顿时其实是同等种折中的Service
Locator模式。将定位并创依赖对象的逻辑直接坐对象中,在PetShop的事例中,不失为一种好法子。毕竟在是事例中,需要借助注入的对象并无极端多。但我们吧堪看是同一栽无奈之服的点子,一旦这种靠注入的逻辑增多,为受程序者带来一定的累,这时就需一个特地的轻量级IoC容器了。

描绘及此地,似乎已退出了“封装变化”的主题。但实则我们用了解,利用抽象的方法封装变化,固然是承诺本着急需变动的德政,但它为特能免去调用者与于调用者相对的耦合关系,只要尚论及到具体目标的缔造,即使引入了工厂模式,但现实的厂子对象的始建仍然是少不了的。那么,对于这样一些早已被包变化的目标,我们尚应有充分利用“依赖注入”的艺术来彻底消除两者之间的耦合。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图