我一直在研究设计模式,并且遇到过访客模式。对我来说真是一件奇怪的事。似乎其唯一目的是即使在向其添加新职责时也可以防止层次结构中的原始类发生更改。只需将这些职责委派给抽象访问者类(或接口)的实现,并有效地进行双重调度,以调用传递正确的动态类型的正确访问者的操作功能,就可以实现这一点。
我已经研究了其他各种问题,讨论了何时以及为何使用访客模式,而我遇到的只是在OOP中,期望您的类数量在增加,而不是每个类中方法的数量在增加,这是“访客模式”专门研究的。
但是,这是真的吗?对我来说,这听起来像是对OOP的过度简化。作为参考,此答案来自here
我还阅读了另一个(较松散的)相关问题here,的答案,这些答案意味着您应该仅在有意义的情况下创建更多类并单独承担责任。组织代码以使一个类的“职责”仅由一个操作定义,而不管操作的类型是什么,这真的有意义吗(即访问者负责每种相关类型的操作的实现) ,还是将责任定义为可以在单个类型上完成的一组操作(将这些责任委托给类型本身)是否更有意义?
如果目标仅仅是减少每个类中的代码行,那么为什么这是一个合理的目标?单独的行数对封装,模块化,代码组织或一般的良好OOP实践没有直接影响,对吧?
编辑: 我现在知道不存在用于OOP的模式,并且我现在知道在某些情况下,访客模式是 best 解决方案。话虽如此,当与OOP一起使用时,它是否会促进较不明智的类层次结构?
答案 0 :(得分:1)
我认为您可能误解了访问者模式和一般模式的用途以及我们有时使用它们的原因。
不是是因为某些面向对象的事情,某些抽象概念或一些隐藏的事实。相反。您应该使用具有行为,隐藏状态以及所有这些的对象,以正确的面向对象的方式对问题进行建模。简而言之,就是 面向对象。 不您应该出去寻找在代码中随机应用的模式,实际上,这本身就是一种反模式:)
现在,有人注意到,我们大多数人倾向于针对某些小问题提供类似的解决方案。这些很有趣,不是因为解决方案如此具有开创性或本身有价值,而是因为它浪费了我们一些时间和智慧,无法一次又一次地解决这些问题。这些是模式。
回到您的问题:如果您不太了解“访客模式”的作用或为什么,或如何/为什么使用它,请不要不用担心。如果遇到需要的问题,您会发现!在这种情况下,没有别的方法会起作用,而您实际上将必须使用它。在所有其他情况下,您不应使用它!
摘要:不,由于某些OOP问题,我们不使用“访客模式”。减少生产线本身并不是一个合法的目标。是的,仅行数并不会影响封装,模块化等。
答案 1 :(得分:0)
从长远来看,面向对象的软件在设计时特别注意SOLID object-oriented principles的情况。设计模式只是这些原则的命名,可重用的应用。
Visitor模式的目的不是防止层次结构中的原始类“即使在添加新职责时也需要更改”。而是将这些类设置为扩展而不进行修改,这是“开放/封闭原则”的完美示例。
但是,要回答您的特定问题:
正确。减少行数不是目标。实际上,我可以向您展示一个类,其中使用“单一职责原则”将增加代码行数。 (请参见下文。)
关于访问者模式,没有“旁边的OOP”。和不。它不会促进较不明智的类层次结构。它使您可以在不修改该类的情况下向其添加行为,从长远来看,这将使您的软件更具可维护性。
这是我答应过的示例(在Ruby中带有结尾的注释):
class OrderTotalCaluculator
attr_reader :order, :tax_calculators
def initialize(order, tax_calculators = [])
@order = order
@tax_calculators = tax_calculators
end
def total
items_subtotal = order.items.sum { |item| item.price * item.quantity }
taxes_subtotal = tax_calculators.sum { |calculator| calculator.calculate(items_subtotal) }
items_subtotal + taxes_subtotal
end
end
class CaliforniaStateSalesTaxCalculator
def self.calculate(amount)
amount * 0.075
end
end
class SanFranciscoCitySalesTaxCalculator
def self.calculate(amount)
amount * 0.01
end
end
total = OrderTotalCalculator.new(
order,
[
CaliforniaStateSalesTaxCalculator,
SanFranciscoCitySalesTaxCalculator
]
).total
总体而言,此代码(实现了Strategy模式)非常好。每个班级都有一个责任。另外,如果美国曾经要征收国家营业税,则OrderTotalCalculator
无需更改。
但是,如果我们仔细观察,OrderTotalCalculator#total
方法将承担多个责任。这是我们清理后的样子:
class OrderTotalCaluculator
attr_reader :order, :tax_calculators
def initialize(order, tax_calculators = [])
@order = order
@tax_calculators = tax_calculators
end
def total
items_subtotal + taxes_subtotal
end
def items_subtotal
@items_subtotal ||= order.items.sum { |item| item.price * item.quantity }
end
def taxes_subtotal
tax_calculators.sum { |calculator| calculator.calculate(items_subtotal) }
end
end
现在,从字面上看,每种方法都负有一个明确定义的命名责任。而且,该班级实际上在增加。所以你去了。
Ruby注释:
@variable
创建一个名为变量的实例变量attr_reader :variable
语法糖来为@variable
实例变量创建一个getter方法@variable ||= statement
会在实例变量中记住该语句的值,以便下次调用该方法时,不会再次执行该语句答案 2 :(得分:-1)
首先我要说我既不是模式专家也不是OOP教条专家。我只是一个每天使用模式和OOP的开发人员。
我几乎同意几乎没有教条主义的任何观点,只是因为迟早坚持教条会使你陷入困境。但是,我确实同意这样一个事实,那就是增加类而不是方法。当然,这是一个过分的简化,并且说一个类不能增加方法是完全错误的。教条主义方法的唯一有效案例是,如果永远不会对您正在使用类建模的对象进行任何更新。不过,通常情况下,一个类定义一个对象,并且可以预期该类表示最终对象,而不是会改变的对象。