我刚开始学习装饰设计模式,不幸的是我不得不通过各种参考来更好地理解装饰器模式,这让我非常困惑。所以,就我的理解而言,我相信这是一个装饰模式
interface IComponent
{
void Operation();
}
class Component : IComponent
{
public void Operation()
{
Console.WriteLine("I am walking ");
}
}
class DecoratorA : IComponent
{
IComponent component;
public DecoratorA(IComponent c)
{
component = c;
}
public void Operation()
{
component.Operation();
Console.WriteLine("in the rain");
}
}
class DecoratorB : IComponent
{
IComponent component;
public DecoratorB(IComponent c)
{
component = c;
}
public void Operation()
{
component.Operation();
Console.WriteLine("with an umbrella");
}
}
class Client
{
static void Main()
{
IComponent component = new Component();
component.Operation();
DecoratorA decoratorA = new DecoratorA(new Component());
component.Operation();
DecoratorB decoratorB = new DecoratorB(new Component());
component.Operation();
Console.Read();
}
}
但下面的代码也可以是Decorator Pattern吗?
class Photo
{
public void Draw()
{
Console.WriteLine("draw a photo");
}
}
class BorderedPhoto : Photo
{
public void drawBorder()
{
Console.WriteLine("draw a border photo");
}
}
class FramePhoto : BorderedPhoto
{
public void frame()
{
Console.WriteLine("frame the photo");
}
}
class Client
{
static void Main()
{
Photo p = new Photo();
p.Draw();
BorderedPhoto b = new BorderedPhoto();
b.Draw();
b.drawBorder();
FramePhoto f = new FramePhoto();
f.Draw();
f.drawBorder();
f.frame();
}
}
我的理解
从我给出的第二个例子中,我们可以调用所有这三个方法,但从第一个例子开始,我无法通过创建单个对象来访问所有这三个方法。
答案 0 :(得分:34)
应该是评论,但我的话太多了。
例如,您有一个对象和界面,如Repository : IRepository
。
public interface IRepository
{
void SaveStuff();
}
public class Repository : IRepository
{
public void SaveStuff()
{
// save stuff
}
}
和客户,可能是其他人写的
class RepoClient
{
public void DoSomething(IRepository repo)
{
//...
repo.SaveStuff();
}
}
一旦你决定,就应该记录对存储库的所有调用。但是您有一个问题:Repository
课程来自外部图书馆,您不想更改该代码。因此,您需要扩展您使用的Repository
行为。您编写RepositoryLogDecorator : IRepository
,并在每个方法内部执行日志记录,如
public class RepositoryLogDecorator : IRepository
{
public IRepository _inner;
public RepositoryLogDecorator(IRepository inner)
{
_inner = inner;
}
public void SaveStuff()
{
// log enter to method
try
{
_inner.SaveStuff();
}
catch(Exception ex)
{
// log exception
}
// log exit to method
}
}
因此,在您将客户端用作
之前var client = new RepoClient();
client.DoSomething(new Repository());
但现在可以使用
var client = new RepoClient();
client.DoSomething(new RepositoryLogDecorator(new Repository()));
请注意,这是一个非常简单的例子。在实际项目中,使用DI容器创建主对象,您可以通过更改某些配置来使用装饰器。
因此,decorator用于扩展对象的功能而无需更改对象或客户端。
装饰者的另一个好处:你的装饰者不依赖Repository
实现。仅取决于接口IRepository
。为什么这是一个优势?如果你以某种方式决定自己编写IRepository
public class MyAwesomeRepository : IRepository
{
public void SaveStuff()
{
// save stuff, but AWESOME!
}
}
您将能够使用已经存在的装饰器自动装饰它
var client = new RepoClient();
client.DoSomethig(new RepositoryLogDecorator(new MyAwesomeRepository()));
想要从真实软件中查看示例? (就像样本,代码很难看,我知道)=> go here
答案 1 :(得分:10)
有PatternCraft series on Youtube解释星际争霸的设计模式,您应该check the video about Decorators here。
在上面的视频中,作者提供了一个Marine
和WeaponUpgrade
的示例。
在游戏中你将拥有Marine
然后你可以升级它的武器:
marine = new WeaponUpgrade(marine);
请注意,你仍然有一个海军陆战队员,它不是一个新的单位,它是与改变其属性的东西相同的单位。
public class MarineWeaponUpgrade : IMarine
{
private IMarine marine;
public MarineWeaponUpgrade(IMarine marine)
{
this.marine = marine;
}
public int Damage
{
get { return this.marine.Damage + 1; } // here
set { this.marine.Damage = value; }
}
}
您可以通过创建一个实现与您的单元相同的接口的类来访问您的单元属性来修改值。
有一个Kata on CodeWars挑战你完成海军陆战队的武器和装甲装饰。
答案 2 :(得分:4)
Per GOF page装饰者设计模式:
动态地将附加职责附加到对象。装饰器为子类化提供了灵活的替代方法,以扩展功能。
在你的第二个例子中,你使用继承来扩展类的行为,我相信这在技术上不是一个Decorator设计模式。
答案 3 :(得分:3)
装饰器模式允许您将特定行为添加到给定类型的单个对象,而不会影响同一类型的其他实例。
在第二个例子中,这是正常继承,该类的所有实例都会继承修改后的行为。
答案 4 :(得分:0)
第二个例子不是装饰模式,因为装饰器模式的一个基本要素是该对象接受其中一种并可能增强它。
第一个例子中的实例是
public DecoratorA(IComponent c)
{
component = c;
}
此外,装饰器模式的目标是创建“一个”对象,然后通过将其传递给不同的过滤器或装饰器来装饰它。 因此行
DecoratorA decoratorA = new DecoratorA(new Component());
应该是
DecoratorA decoratorA = new DecoratorA(component );