技术开发 频道

.NET中的设计模式之一步步发现装饰模式

  下面是我们实现的SecureMessageWriter和EncryptedMessageWriter。

class SecureMessageWriter : IMessageWriter {
        
private string message;
        
//被装饰者
        
private IMessageWriter messageWriter;
        
public SecureMessageWriter(IMessageWriter msgWriter) {
            this.messageWriter
= msgWriter;
        }
        
public string Message {
            
set { message = value; }
        }
        
public void WriteMessage(string filePath) {
            
if (this.ValidateUser()) {//添加新的行为
                
//正如你所见,在调用被装饰者的标准方法前我们添加了验证行为
                messageWriter.Message
= this.message;
                messageWriter.WriteMessage(filePath);
            }
            
else
                Console.WriteLine(
"");
        }
        
private bool ValidateUser() {            
            
//验证用户代码
            return
true;
        }
    }
    class EncryptedMessageWriter : IMessageWriter {
        
private string message;
        
//被装饰者
        
private IMessageWriter msgWriter;
        
public EncryptedMessageWriter(IMessageWriter msgWriter) {
            this.msgWriter
= msgWriter;
        }
        
public string Message {
            
set { message = value; }
        }
        
public void WriteMessage(string filePath) {            
            this.msgWriter.Message
= "encrytedMsgInBase64";//加密信息
            
//被装饰得行为
            this.msgWriter.WriteMessage(filePath);
        }
        
private string GetPassword() {
            Console.WriteLine(
"Please provide security password");
            return Console.ReadLine();
        }
    }

 

  这里有问题吗?????

  我刚刚说这个模式有四个参与者,我已向你展示了Component(IMessageReader,IMessageWriter),ConcreteComponent(MessageReader,MessageWriter)和ConcreteDecorator(SecureMessageWriter,EncryptedMessageWriter)。但Decorator那里去了?在我们的例子中,我们仅仅添加了已存在的行为,没有引进新的行为。我们没有改变其他结构。在这种情况下,我们忽略了实现Decorator,并沿用主要层次结构。这里我不在展示读有关的类,它们仅仅是反向处理。

  我们学到了什么

  现在如果我需要一个经过用户验证的简单信息写操作,我会这么做:

IMessageWriter msgWriter = new SecureMessageWriter(new MessageWriter());

 

  我用SecureMessageWriter装饰了MessageWriter,它现在在写信息到磁盘前会先验证用户。如果同时需要验证用户和加密信息,我会这么做:

IMessageWriter msgWriter = new SecureMessageWriter(new EncryptionMessageWriter(new MessageWriter()));

 

  1. 装饰器使我们在大多数情况下避免构造复杂基类,撰写大量的代码。

  2. 装饰器允许我们以不同的顺序进行不同的组合,这在别的方法来说不太容易。

  3. 相比为不同的行为与合并实现不同的基类,我们可以按需实现单独的需求行为。

  回到现实

  这一部分我们会看到装饰器模式在实际中应用的例子。

  同步包

  善于使用.ET中旧集合类,如Queue,ArrayList等的人可能还记得许多类提供的(集合)同步函数。它把集合实例自己作为参数传入,并返回一个同步集合。尽管集合类自己并不同步,但这个方法返回的实际上是从集合类自己继承过来的一个装饰器。例如,在下面代码中当我们调用Syncrhonized(a1)时,我们会收到一个从ArrayList继承来的SyncArrayList的实例。

ArrayList al = new ArrayList();
al
= ArrayList.Synchronized(al);

 

  SyncArrayList存储通过属性_list传递的ArrayList,并且按照同样的方法重载不同的实例方法。

public override int Add(object value) {
       lock (this._root){
           return this._list.Add(value);
       }
   }

 

  注意事项

  按照此法创建同步包时,要注意叫做“自死锁”现象,这意味着一个占有锁的线程进入另一格方法(或者递归),然后又试图获取同一个对象的锁。在Windows中,如果你使用.NET实施监控,或者内核级命名,或者无名互斥,全部都有重入机制(即递归)。所以你不会遇到此类问题,但是在其他环境(如Linux)中编程时,默认互斥类型是快速互斥(一种非递归互斥),你的代码就可能成为“自死锁”的受害者。假如使用消息,即使在Windows上,它一样没有自我意识,如果你不注意,就会给你带来这个问题。当然,对于一个简单信号,比如n=1,在第二次访问时,你一样会遇到“自死锁”。

  同理,你可以为你的集合类实现一个只读包。不像我们到现在所看到的,与其说他们是向类上添加功能,倒不如说去掉一些。例如在重载方法Add()中可能抛出操作不支持的异常。.NET提供了ReadOnlyCollection,它用于包装泛型列表。Java则提供了只读包,如UnmodifiableCollection,UnmodifiableSet等等。

  Java中,你可以按照下面方式为很多集合类型获取同步包。

  Java和.NET中的IO

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

  同样,在Java中,BufferedReader和FilterReader是Reader的装饰者。而BufferedReader进一步被LineNumberReader装饰。FilterReader被PushbackReader装饰。

  收获了什么

  装饰器模式允许我们在实现中提供扩展点。你可能注意到,在实现装饰者时,我从来没有涉及到Component类。因此,即使它并不拥有类,一样能通过动态添加行为类装饰它,甚至是递归的方式。这些额外的行为可能在几处行为之前或之后添加,或者这种行为可能被阻止。装饰者能在类中提供新方法,甚至新属性。但装饰器同样有一些问题。

  使用装饰器的程序员必须明白它们的意图,否则他最终可能会使用一些毫无意义的组合或序列。比如在我们的场景中,如果程序员按照这种方式定义序列:信息先被压缩,然后加密,最后验证,它将毫无意义。在现实场景中,有些组合或次序可能是灾难性的。

  测试一个装饰类需要提供一个模拟的被装饰类。因为我们还没有涉及到测试,所以这里就不说了。

  该模式在基本行为上添加一些职责。虽然给装饰者添加新的属性或方法完全合法,但这种方法导致基础类处理问题的灵活性,因为你需要用到具体的实例。

0
相关文章