装饰器模式变体






4.50/5 (2投票s)
装饰器模式的一个变体,以及扩展方法的使用。
引言
我们都听说过设计模式。它们是解决/改进常见开发场景的已知路径。模式可以修改和扩展以适应广泛的需求。
这里提供一个使用接口的装饰器模式变体。
背景
装饰器模式允许扩展无法继承或方法不可重写的类的行为。装饰器类持有对 sealed
类的引用,当调用装饰器类的方法时,它将执行其代码并调用引用的对象。结果是对 sealed
类的扩展。
使用代码
假设我们想要构建一个模块化的日志库,我们可以将装饰器模式调整到我们的需求。一个好的地方是,每个装饰器类都可以单独使用,也可以与其他装饰器堆叠使用。
这是所有装饰器之间的共享接口
using System;
namespace George.Decorator.ConsoleApp
{
public interface ILoggingProvider
{
string Message {get;set;}
string Source {get;set;}
void Log();
}
}
现在是装饰器
控制台装饰器
using System;
namespace George.Decorator.ConsoleApp
{
public class ConsoleDecorator : ILoggingProvider
{
private ILoggingProvider _iLoggingProvider = null;
private string _message, _source = null;
//empty contructor for a traditional object
public ConsoleDecorator(){}
//init class members and decorated interface
public ConsoleDecorator(ILoggingProvider iLoggingProvider)
{
this._iLoggingProvider = iLoggingProvider;
Message = iLoggingProvider.Message;
Source = iLoggingProvider.Source;
}
//both current and decorated inteface method are invoked
public void Log()
{
if(_iLoggingProvider != null)
{
WriteToConsole(_iLoggingProvider.Source,
_iLoggingProvider.Message);
_iLoggingProvider.Log();
}
else
{
WriteToConsole(Source,Message);
}
}
private void WriteToConsole(string source, string message)
{
Console.WriteLine("--------------------------------");
Console.WriteLine(string.Format("Source: {0}",source));
Console.WriteLine(string.Format("Message: {0}",message));
Console.WriteLine("--------------------------------");
Console.Write(Environment.NewLine);
}
public string Message {
get {
return _message;
}
set {
_message = value;
}
}
public string Source {
get {
return _source;
}
set {
_source = value;
}
}
}
}
事件日志装饰器
using System;
using System.Diagnostics;
namespace George.Decorator.ConsoleApp
{
public class EventLogDecorator: ILoggingProvider
{
private ILoggingProvider _iLoggingProvider = null;
private string _message, _source = null;
public EventLogDecorator(){}
public EventLogDecorator(ILoggingProvider iLoggingProvider)
{
this._iLoggingProvider = iLoggingProvider;
Message = iLoggingProvider.Message;
Source = iLoggingProvider.Source;
}
public void Log()
{
if(_iLoggingProvider!=null)
{
CheckIfSourceExist(_iLoggingProvider.Source);
EventLog.WriteEntry(_iLoggingProvider.Source,
_iLoggingProvider.Message);
_iLoggingProvider.Log();
}
else
{
CheckIfSourceExist(Source);
EventLog.WriteEntry(Source, Message);
}
}
private void CheckIfSourceExist(string source)
{
if(!EventLog.SourceExists(source))
EventLog.CreateEventSource(source, "Application");
}
public string Message {
get {
return _message;
}
set {
_message = value;
}
}
public string Source {
get {
return _source;
}
set {
_source = value;
}
}
}
}
Winform装饰器
using System;
using System.Windows.Forms;
namespace George.Decorator.ConsoleApp
{
public class WinFormDecorator: ILoggingProvider
{
private ILoggingProvider _iLoggingProvider = null;
private string _message, _source = null;
public WinFormDecorator(){}
public WinFormDecorator(ILoggingProvider iLoggingProvider)
{
this._iLoggingProvider = iLoggingProvider;
Message = iLoggingProvider.Message;
Source = iLoggingProvider.Source;
}
public string Message {
get {
return _message;
}
set {
_message = value;
}
}
public string Source {
get {
return _source;
}
set {
_source = value;
}
}
public void Log()
{
if(_iLoggingProvider != null)
{
ShowMessageBox(_iLoggingProvider.Message,
_iLoggingProvider.Source);
_iLoggingProvider.Log();
}
else
{
ShowMessageBox(Message,Source);
}
}
private void ShowMessageBox(string message, string source)
{
MessageBox.Show(message,source);
}
}
}
现在我们可以以独立模式使用装饰器对象
class Program
{
public static void Main(string[] args)
{
ConsoleDecorator consoleDecorator = new ConsoleDecorator();
EventLogDecorator eventLogDecorator = new EventLogDecorator();
WinFormDecorator windowsFormDecorator = new WinFormDecorator();
//in case of an information it will be enough the console windows
consoleDecorator.Message = "Hi, here's the console";
consoleDecorator.Source = "Console";
consoleDecorator.Log();
//if we want to log the message also on the eventviewer
eventLogDecorator.Message = "Hi, here's the event viewer";
eventLogDecorator.Source = "Event viewer";
eventLogDecorator.Log();
//or if we want to log the message also on the messagebox
windowsFormDecorator.Message = "Hi, here's the windows form";
windowsFormDecorator.Source = "Message Box";
windowsFormDecorator.Log();
或者我们可以通过使用装饰器模式组合一个日志提供程序块
//decorator pattern variant
consoleDecorator = new ConsoleDecorator();
consoleDecorator.Message = "Hi, here's the console again";
consoleDecorator.Source = "Console";
eventLogDecorator = new EventLogDecorator(consoleDecorator);
windowsFormDecorator = new WinFormDecorator(eventLogDecorator);
//print out the output using all the providers
windowsFormDecorator.Log();
现在我们来看看为什么只有通过使用接口编程才能获得好处。在这种情况下,我们可以使用任何实现 ILoggingProvider
接口的对象来构建日志,而在其他情况下,例如使用扩展方法时
using System;
using System.Windows.Forms;
using System.Diagnostics;
namespace George.Decorator.ConsoleApp
{
public static class ConsoleDecoratorExtensions
{
//Adding event viewer logging
public static void LogWithEventViewer(this ILoggingProvider consoleDecorator)
{
if(!EventLog.SourceExists(consoleDecorator.Source))
EventLog.CreateEventSource(consoleDecorator.Source, "Application");
EventLog.WriteEntry(consoleDecorator.Source,consoleDecorator.Message);
consoleDecorator.Log();
}
//adding messagebox alert
public static void LogWithMessageBox(this ConsoleDecorator consoleDecorator)
{
MessageBox.Show(consoleDecorator.Message,consoleDecorator.Source);
consoleDecorator.Log();
}
}
}
接口的额外价值在于,现在我们有了适用于实现它的所有类的扩展方法,针对类的扩展方法将仅适用于该类。
//using extension methods
consoleDecorator.LogWithEventViewer();
consoleDecorator.LogWithMessageBox();
//interface extension method
windowsFormDecorator.LogWithEventViewer();
关注点
设计模式是一个引人入胜的话题,它们为解决日常问题提供了优雅的解决方案。在这种情况下,装饰器模式的一个变体有助于构建可以独立的对象(通常装饰器仅用于扩展对象),或者具有某种继承关系的对象。