【发布时间】:2012-03-10 22:31:16
【问题描述】:
我相信理解装饰者和访问者设计模式的意图。
虽然我可以列出以下差异
- 装饰器处理对象,访问者处理复合结构,
- 装饰器是结构设计模式,访问者是行为设计模式。
当我深入思考时,我无法说服自己两者之间的真正区别是什么。
【问题讨论】:
标签: design-patterns decorator visitor-pattern
我相信理解装饰者和访问者设计模式的意图。
虽然我可以列出以下差异
当我深入思考时,我无法说服自己两者之间的真正区别是什么。
【问题讨论】:
标签: design-patterns decorator visitor-pattern
嗯,它们实际上是不同的!
当您想使用一些新的、或多或少透明的功能(例如验证或缓存)来增强现有对象时,您可以使用 Decorator。请参阅此处的示例:Should I extend ArrayList to add attributes that isn't null?
另一方面,Visitor 用于当您具有类层次结构并希望根据具体类型运行不同的方法但避免使用 instanceof 或 typeof 运算符时。查看真实示例:Is This Use of the "instanceof" Operator Considered Bad Design?
装饰器作用于对象,访问者作用于复合结构,
Visitor 适用于继承层次结构,Composite 是一种不同的 GoF 设计模式。
装饰器是结构设计模式,访问者是行为设计模式。
没错,但它对理解它们的工作原理并没有真正的帮助?
【讨论】:
设计模式并不是按照实现差异来分类的,而是按照你应该在什么时候使用一种或另一种来分类。
它们的用途完全不同:
【讨论】:
它们都向现有对象“添加功能”,而不修改原始类。区别在于:
使用装饰器您添加包装该对象具有的基本功能的功能(例如,除了执行一些基本操作外,还将其写入日志,除了将文件写入磁盘外,还要对其进行加密) .这也允许我们创建不同的装饰器组合,而无需对每个可能的场景进行子类化。
使用访问者 您添加了一个全新的行为,您不想将其定义为基本组件类本身的一部分(甚至不作为基本功能的包装器),例如因为单个责任原则、开闭原则等
当同一类型的不同子类之间的行为不同时,它特别有用(如果没有任何复杂的子类结构而只有一个类,您可以创建一个新类并通过组合包含原始类并仍然实现目标不影响或修改原始类)。这样你就可以避免像if (a is ConcreteClass1) {...} else if (a is ConcreterClass2) {...} 这样的代码,而无需编写虚拟方法。
由于这种差异,客户端代码使用装饰器调用在基本组件类的接口上定义的相同方法,现在它只是“装饰”了额外的功能,而访问者客户端调用了一些通用的“接受”方法并向其发送访问者。
【讨论】:
我喜欢认为装饰器允许避免继承然后扩展类,这是 OOP 的一般原则,更喜欢聚合而不是继承,尽管您确实以某种方式继承。这是一个过于简单的例子
abstract class Chef{
public abstract void Prepare();
}
class CookieMaker:Chef{ //Concrete class
public override void Prepare()
{
//Bake in Oven
}
}
// Decorator class
// This chef adds chocolate topping to everything
class ChocoChef:Chef{
public ChocoChef(Chef mychef)
{
this.chef = mychef;
}
public override void Prepare()
{
// Add chocolate topping first
chef.Prepare()
}
}
为了篇幅,我删减了一些细节。例如,您可以抽象出添加任何类型浇头的厨师,然后 ChocoChef 成为其具体类。现在,无论您准备什么,ChocoChef 都会添加巧克力配料。因此,现在您可以通过将相应的 Chef 传递给其构造函数来获得巧克力饼干或巧克力蛋糕。另一方面,访问者作用于对象并根据它正在访问的对象决定做某事。
class Student{
// Different visitors visit each student object using this method
// like prize distributor or uniform inspector
public Accept(IVisitor v)
{
v.Visit(this)
}
}
// Visitor visits all student OBJECTS
class PrizeDistributor:IVisitor{
public override void Visit(Student s)
{
// if(s has scored 100)
// Award prize to s
}
}
【讨论】:
按照我的解释,访问者代表了我们可能想要对一个对象采取的行动,但这些行动不一定是一个对象固有的,而且在关系上是相当水平的。例如,我可以为汽车“做营销宣传”,但我不会将汽车对象编程为具有“createMarketingPitch”函数,因为这将是在我的汽车对象上创建许多函数的滑坡。
另一方面,装饰器是一种将功能分层到现有对象之上的模式,这是一种垂直关系,可以修改对象在调用其正常函数时的行为方式。此外,虽然访问者被编码为使用一类对象,但装饰器可以分配给对象的特定实例,以便同一类型的不同实例表现不同。
【讨论】:
装饰器模式可用于静态扩展(装饰)某个对象的功能,或者在某些情况下在运行时独立于同一类的其他实例,提供一些基础在设计时完成
何时使用装饰器模式?
相关帖子:
When to Use the Decorator Pattern?
访问者设计模式是一种将算法与其操作的对象结构分离的方法。这种分离的实际结果是能够在不修改现有对象结构的情况下向现有对象结构添加新操作。这是遵循开闭原则的一种方式。
何时使用访客模式?
相关帖子:
When should I use the Visitor Design Pattern?
有用的链接:
sourcemaking装饰者文章
oodesign访客文章
sourcemaking访客文章
【讨论】:
我想到装饰器模式的方式是当我想在运行时向对象添加功能时。我可以在程序运行时向对象添加行为,方法是将对象包装在可以扩展其方法的装饰器类中。
对于访问者模式,当我必须对“一组”相同类型的对象进行操作并收集信息时,我喜欢它。假设我有 10 个具体的蔬菜类,我想知道所有 10 个蔬菜的总价。我可以使用访问者模式“访问”每个蔬菜对象,在迭代结束时我会得到总价。当然,您也可以使用该模式将某些操作与对象分离。
【讨论】:
是的,它们都在运行时向现有系统添加了一些功能,并试图减少对动态变化的反应(在好的意义上),但有一些区别。
Visitor 主要是尊重 OCP(有时也尊重 SRP),以使系统更加灵活。随着程序的发展,您可以添加任何访客,而无需更改现有系统。但是,您需要事先以这种方式设计系统。您不能向已经运行的系统添加新的访问者类(或模式)并期望它在不重新编译、重新测试或其他任何方式的情况下工作。
另一方面,您可以使用装饰器来丰富现有系统功能,方法是将抽象基类(您已经拥有)包装在装饰器中,并将您丰富的功能作为单独的对象,以便您可以根据需要创建。而且,从语义上来说,Decorator 是指某物的外观。
更喜欢哪一个? IMO,回答这个问题可能会更有帮助。对我来说,我不喜欢装饰者使用基类的方式。它既使用继承又使用聚合。如果您需要更改这个(wrapee)类,您最终会重新编译整个层次结构/模块。但它很方便,您可以在设计时间之后更改行为。另一方面,在访问者模式中,我不喜欢在访问者实现中了解每个具体类型的想法。当你添加一个新的基类类型时,你还需要去更改 Visitor 类来添加它。但是,当您需要在不改变结构的情况下将代码注入到现有系统中,或者您需要在类中分离关注点(单用户响应)时,它会很有用。
最后,是什么让 Visitor 比常规继承更好?要看。使用继承,您将更加依赖于接口签名。使用访问者使您的访问者类依赖于具体类。更不用说您使用访问者添加更多行为而不更改现有模块签名而不是在现有类中实现新接口。
【讨论】: