当以面向对象的方式实现大海捞针搜索时,您基本上有三种选择:
1. needle.find(haystack) 2. haystack.find(needle) 3. searcher.find(needle, haystack)
你更喜欢哪个?为什么?
我知道有些人更喜欢第二种选择,因为它避免引入第三种物体.然而,我不禁感到第三种方法在概念上更"正确",至少如果你的目标是塑造"现实世界".
在哪种情况下,您认为引入辅助对象是合理的,例如本例中的搜索器,何时应避免使用?
在这三个中,我更喜欢选项#3.
该单一职责原则使我不希望把搜索我的DTO或模型的能力.他们的责任是成为数据,而不是发现自己,也不需要了解干草堆,也不知道干草堆知道针头.
对于它的价值,我认为大多数OO从业者需要花费很长时间来理解为什么#3是最佳选择.在我真正理解它之前,我做了OO十年.
@wilhelmtell,C++是极少数具有模板专业化的语言之一,使这样的系统真正起作用.对于大多数语言而言,通用的"查找"方法将是一个可怕的想法.
通常应该将动作应用于您正在执行的操作...在这种情况下是haystack,所以我认为选项2是最合适的.
你还有第四种选择,我认为比替代3更好:
haystack.find(needle, searcher)
在这种情况下,它允许您提供要作为操作的一部分进行搜索的方式,因此您可以将操作与正在操作的对象保持一致.
还有另一种选择,即C++的STL使用的方法:
find(haystack.begin(), haystack.end(), needle)
我认为这是一个很好的例子,C++喊"在你的脸上!" OOP.这个想法是OOP不是任何类型的银弹; 有时事情最好用行动来描述,有时候就物体而言,有时既不是,也不是两者兼而有之.
Bjarne Stroustrup在TC++ PL中说,当你设计一个系统时,你应该努力在有效和高效的代码约束下反映现实.对我来说,这意味着你永远不应盲目追随任何事情.想想手头的事情(干草堆,针)和我们所处的背景(搜索,这就是表达的内容).
如果重点是搜索,那么使用强调搜索的算法(动作)(即灵活地适应干草堆,海洋,沙漠,链表).如果重点是haystack,请将find方法封装在haystack对象中,依此类推.
也就是说,有时你会有疑问并且很难做出选择.在这种情况下,面向对象.如果您稍后改变主意,我认为从对象中提取操作然后将操作拆分为对象和类更容易.
遵循这些准则,您的代码将更清晰,更美观.
我会说选项1完全没了.代码应该以一种告诉你它的作用的方式阅读.选项1让我觉得这针会找到我的大海捞针.
如果大海捞针要包含针,选项2看起来不错.ListCollections总是包含ListItems,所以做collection.find(item)是自然而富有表现力的.
我认为在以下情况下引入辅助对象是合适的:
您无法控制
IE 中有问题的对象的实现:search.find(ObsecureOSObject,file)
对象
IE 之间没有规律或合理的关系:nameMatcher.find(houses,trees.name)
我和布拉德就这个问题.我在越来越复杂的系统上工作的越多,我就越需要真正解耦对象.他是对的.显而易见的是针头不应该对干草堆知之甚少,所以1肯定是出来的.但是,大海捞针应该对针头一无所知.
如果我正在为大海捞针建模,我可能会把它作为一个集合来实现 - 但是作为干草或稻草的集合 - 而不是针的集合!但是,我会考虑到在大海捞针中丢失的东西,但我对这些东西一无所知.我认为最好不要让干草堆本身寻找物品(无论如何,干草堆有多聪明).对我来说正确的做法是让干草堆里面有一系列东西,但不是稻草或干草,或者其他东西都是大海捞针的本质.
class Haystack : ISearchableThingsOnAFarm { ICollectionmyHay; ICollection stuffLostInMe; public ICollection Hay { get { return myHay; } } public ICollection LostAndFound { get { return stuffLostInMe; } } } class Needle : IStuffSmallEnoughToBeLostInAHaystack { } class Farmer { Search(Haystack haystack, IStuffSmallEnoughToBeLostInAHaystack itemToFind) }
实际上我会更多地打字和抽象到接口,然后我意识到我有多疯狂.感觉就像我在大学的CS课......:P
你明白了.我认为尽可能松散耦合是一件好事,但也许我有点沮丧!:)
如果Needle和Haystack都是DAO,则选项1和2是不可能的.
这样做的原因是DAO应该只负责保存他们正在建模的现实世界对象的属性,并且只有getter和setter方法(或者只是直接属性访问).这使得将DAO序列化为文件,或者创建通用比较/通用副本的方法更容易编写,因为代码不会包含一大堆"if"语句来跳过这些辅助方法.
这只留下选项3,大多数人都同意这是正确的行为.
选项3有一些优点,最大的优点是单元测试.这是因为现在可以很容易地模拟Needle和Haystack对象,而如果使用选项1或2,则必须在执行搜索之前修改Needle或Haystack的内部状态.
其次,当搜索者现在在一个单独的类中时,所有搜索代码都可以保存在一个地方,包括常见的搜索代码.如果将搜索代码放入DAO中,则常用搜索代码将存储在复杂的类层次结构中,或者仍然存储在Searcher Helper类中.
这完全取决于变化的内容和保持不变的内容.
例如,我正在研究一个(非OOP)框架,其中查找算法根据针的类型和干草堆而不同.除了在面向对象的环境中需要双重调度这一事实之外,它还意味着编写任何一个needle.find(haystack)
或写入都没有意义haystack.find(needle)
.
另一方面,您的应用程序可以愉快地将查找委托给两个类中的任何一个,或者完全坚持使用一个算法,在这种情况下,决策是任意的.在那种情况下,我更喜欢这种haystack.find(needle)
方式,因为将这一发现应用于大海捞针似乎更合乎逻辑.
当以面向对象的方式实现大海捞针搜索时,您基本上有三种选择:
needle.find(干草堆)
haystack.find(针)
searcher.find(针,干草堆)
你更喜欢哪个?为什么?
纠正我,如果我错了,但在所有三个例子你已经拥有你正在寻找的针的引用,所以这不就是有点像找你的眼镜时,他们坐在你的鼻子?:p
除了旁白,我认为这实际上取决于你认为大海捞针在给定领域内的责任.我们只关心它是一个含有针的东西(一个集合,基本上)?然后haystack.find(needlePredicate)很好.否则,farmBoy.find(谓词,haystack)可能更合适.