当以面向对象的方式实现大海捞针的搜索时,您实际上有三种选择:
1. needle.find(haystack)
2. haystack.find(needle)
3. searcher.find(needle, haystack)
你更喜欢哪个?为什么?
我知道有些人喜欢第二种选择,因为它避免引入第三个对象。但是,至少在您的目标是为“真实世界”建模的过程中,我不禁会觉得第三种方法在概念上更“正确”。
您认为在什么情况下引入助手对象是合理的,例如本例中的搜索器,何时应避免使用它们?
当以面向对象的方式实现大海捞针的搜索时,您实际上有三种选择:
1. needle.find(haystack)
2. haystack.find(needle)
3. searcher.find(needle, haystack)
你更喜欢哪个?为什么?
我知道有些人喜欢第二种选择,因为它避免引入第三个对象。但是,至少在您的目标是为“真实世界”建模的过程中,我不禁会觉得第三种方法在概念上更“正确”。
您认为在什么情况下引入助手对象是合理的,例如本例中的搜索器,何时应避免使用它们?
Answers:
通常情况下,应该将操作应用于您正在执行的操作...在这种情况下,应该选择大海捞针,因此我认为选项2最合适。
您还有第四个替代方案,我认为它比替代方案3更好:
haystack.find(needle, searcher)
在这种情况下,它允许您提供要作为操作一部分进行搜索的方式,因此可以将操作与正在操作的对象保持在一起。
还有另一种选择,这是C ++的STL使用的方法:
find(haystack.begin(), haystack.end(), needle)
我认为这是C ++大喊大叫的一个很好的例子!面向对象。想法是,面向对象编程不是任何形式的灵丹妙药。有时最好用动作来描述事物,有时用对象来描述,有时两者都不是,有时两者都可以。
Bjarne Stroustrup在TC ++ PL中表示,在设计系统时,您应努力在有效代码的约束下反映现实。对我来说,这意味着您切勿盲目跟随任何事情。考虑一下即将发生的事情(干草堆,针叶树)以及我们所处的环境(搜索,这就是表达的意思)。
如果重点是搜索,则使用强调搜索的算法(动作)(即灵活地适合大海捞针,海洋,沙漠,链表)。如果重点是干草堆,则将find方法封装在干草堆对象中,依此类推。
也就是说,有时您会感到疑惑,并且很难做出选择。在这种情况下,应面向对象。如果您以后改变主意,我认为从对象提取动作然后将动作拆分为对象和类会更容易。
遵循这些准则,您的代码将更加清晰,美观。
我要说的是选项1完全没有了。该代码应以告诉您的方式进行阅读。选项1让我认为这针将要去找我一个大海捞针。
如果要在大海捞针中放置草堆,则选项2看起来不错。ListCollections总是包含ListItems,因此执行collection.find(item)是自然而富有表现力的。
我认为在以下情况下适当地引入辅助对象:
我和布拉德在一起。我在极其复杂的系统上工作的越多,就越需要真正分离对象。他是对的。很明显,一根针对干草堆一无所知,因此1肯定已经淘汰了。但是,大海捞针对针头一无所知。
如果要对干草堆建模,则可以将其实现为一个集合-但可以作为干草或稻草的集合-而不是针类的集合!但是,我会考虑到这些东西确实会丢失在大海捞针中,但是我对这些东西到底是什么一无所知。我认为最好不要让大海捞针本身寻找物品(无论如何,大海捞针多么聪明)。对我而言,正确的方法是让干草堆展示其中的所有物品,但不是秸秆,干草或任何赋予干草堆本质的东西。
class Haystack : ISearchableThingsOnAFarm {
ICollection<Hay> myHay;
ICollection<IStuffSmallEnoughToBeLostInAHaystack> stuffLostInMe;
public ICollection<Hay> Hay {
get {
return myHay;
}
}
public ICollection<IStuffSmallEnoughToBeLostInAHayStack> 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类中。
当以面向对象的方式实现大海捞针的搜索时,您实际上有三种选择:
针刺发现(干草堆)
haystack.find(needle)
searcher.find(针,干草堆)
你更喜欢哪个?为什么?
纠正我,如果我错了,但在所有三个例子你已经拥有你正在寻找的针的引用,所以这不就是有点像找你的眼镜时,他们坐在你的鼻子?:p
撇开双关语,我认为这实际上取决于您认为大海捞针在给定范围内的责任。我们是否只是在关心包含针的东西(本质上是一个集合)?然后haystack.find(needlePredicate)很好。否则,farmBoy.find(predicate,haystack)可能更合适。
needle可能是一种类型。但是含糊不清导致人们相信这可能是鸡与蛋的情况。
引用SICP的伟大作者,
必须编写程序供人们阅读,并且只能偶然地使机器执行
我更喜欢同时使用方法1和2。以红宝石为例,它附带了.include?这样的用法
haystack.include? needle
=> returns true if the haystack includes the needle
不过有时,纯粹出于可读性原因,我想将其翻转。Ruby没有提供一种in?方法,但是它是单行的,所以我经常这样做:
needle.in? haystack
=> exactly the same as above
如果强调干草堆或搜索操作“更重要”,我更喜欢写include?。通常,无论大海捞针还是搜索都不是您真正关心的,只是对象存在而已-在这种情况下,我发现in?更好地传达了程序的含义。
我可以想到前两种口味中的任何一种都有意义的情况:
如果针头需要进行预处理(如Knuth-Morris-Pratt算法中那样),则needle.findIn(haystack)(或pattern.findIn(text))是有意义的,因为针头对象拥有为算法有效运行而创建的中间表
如果干草堆需要进行预处理(例如尝试),则haystack.find(needle)(或words.hasAWordWithPrefix(prefix))效果更好。
在以上两种情况下,针头或干草堆之一都知道搜索。此外,他们俩彼此了解。如果您不希望针头和干草堆相互了解或进行搜索,则searcher.find(needle, haystack)比较合适。
该问题的答案实际上应取决于解决方案所针对的领域。
如果您碰巧在物理草垛中模拟物理搜索,则可能有此类
太空
知道哪些物体位于哪个坐标处,从而
实现自然法则(转换能量,检测碰撞等)
针,稻草
位于太空中,
对力产生反作用
搜寻者
与空间互动:
移动手,施加磁场,烧干草,施加X射线,寻找针头...
因此 seeker.find(needle, space) 或 seeker.find(needle, space, strategy)
大海捞针恰好在您要寻找针头的地方。当您将空间抽象为一种虚拟机(认为是矩阵)时,您可以使用干草堆代替空间来获得上述内容(解决方案3 / 3b):
seeker.find(needle, haystack) 要么 seeker.find(needle, haystack, strategy)
但是矩阵是域,如果您的针头不能放在其他任何地方,则只能用干草堆代替。
再说一次,那只是一种动物。有趣的是,这为全新的方向开辟了思路:
1.为什么首先松开针头?您可以更改流程,所以不会放弃它吗?
2.您是否必须找到丢失的针头,还是可以简单地再买一根针而忘记第一根呢?(然后,如果一段时间后针头溶解,那将是很好的选择)
。3.如果您定期松动针头并且需要再次找到它们,则可能需要
制造能够找到自己的针,例如,他们经常问自己:我迷路了吗?如果答案为是,他们会将GPS计算的位置发送给某人或开始发出哔哔声或其他声音:
needle.find(space)或needle.find(haystack) (解决方案1)
在每个稻草上安装带有照相机的干草堆,之后您可以询问干草堆蜂巢的心智是否最近看过针:
haystack.find(needle)(解决方案2)
将RFID标签贴到您的针上,这样就可以轻松地对它们进行三角剖分
就是说,在您的实现中,您已经在某种程度上制造了针,干草堆以及大多数情况下的矩阵。
因此,请根据您的域来决定:
haystack.find(needle),但应该有一个搜索器字段。
我知道依赖注入非常流行,所以@Mike Stone的haystack.find(needle,searcher)被接受并不奇怪。但是我不同意:选择哪种搜索器对我来说似乎是一个决定。
考虑两个ISearcher:MagneticSearcher遍历整个体积,以与磁体强度一致的方式移动和步进磁体。QuickSortSearcher将堆栈分为两部分,直到其中一个子堆中的针明显为止。搜索者的正确选择可能取决于干草堆的大小(例如,相对于磁场),针如何进入干草堆(即,针的位置是真正随机的还是偏斜的?)等。
如果您有haystack.find(needle,searcher),那么您的意思是“最好的选择是在干草堆之外进行的。” 我认为这可能不正确。我认为“干草堆知道如何最好地自我搜索”的可能性更大。添加设置器,如果您需要覆盖或进行测试,仍然可以手动注入搜索器。
您还有第四个替代方案,我认为它比替代方案3更好:
haystack.find(needle, searcher)
我明白你的意思,但是如果searcher实现一个搜索接口,该接口允许搜索除干草堆以外的其他类型的对象,并在其中找到除针头之外的其他东西,该怎么办?
该接口还可以使用不同的算法来实现,例如:
binary_searcher.find(needle, haystack)
vision_searcher.find(pitchfork, haystack)
brute_force_searcher.find(money, wallet)
但是,正如其他人已经指出的那样,我还认为,只有当您实际上具有多个搜索算法或多个可搜索或可找到的类时,这才有用。如果不是这样,我同意haystack.find(needle)它的简单性会更好,因此我愿意为此牺牲一些“正确性”。
确实是2和3的混合。
一些干草堆没有专门的搜索策略;一个例子是数组。找到某物的唯一方法是从头开始并测试每一项,直到找到所需的东西为止。
对于这种情况,自由函数可能是最好的(例如C ++)。
一些干草堆可以对它们施加专门的搜索策略。可以对数组进行排序,例如,允许您使用二进制搜索。一个自由函数(或一对自由函数,例如sort和binary_search)可能是最好的选择。
一些草垛将集成搜索策略作为其实施的一部分;例如,关联容器(哈希或有序集合和映射)都可以。在这种情况下,查找可能是必不可少的查找方法,因此它可能应该是一种方法,即haystack.find(needle)。
干草堆可以包含东西。一种类型的东西是检针器,它是负责搜索东西的东西,可以接受一堆东西作为在哪里找到东西的源。发现者还可以接受需要寻找的东西的东西描述
因此,最好是,对于灵活的解决方案,您可以执行以下操作:IStuff接口
干草堆= IList <IStuff>针:IStuff
Finder .Find(IStuff stuffToLookFor,IList <IStuff> stuffsToLookIn)
在这种情况下,您的解决方案将不仅仅局限于针和干草堆,而是可用于实现该接口的任何类型
因此,如果您想在海洋中找到一条鱼,就可以。
var结果= Finder.Find(鱼类,海洋)
如果您有对针头物体的参考,为什么要搜索它?:)问题域和用例告诉您,不需要在大海捞针中的确切位置(就像从list.indexOf(element)可以得到的一样),您只需要一根针即可。而且您还没有。所以我的答案是这样的
Needle needle = (Needle)haystack.searchByName("needle");
要么
Needle needle = (Needle)haystack.searchWithFilter(new Filter(){
public boolean isWhatYouNeed(Object obj)
{
return obj instanceof Needle;
}
});
要么
Needle needle = (Needle)haystack.searchByPattern(Size.SMALL,
Sharpness.SHARP,
Material.METAL);
我同意,有更多可能的解决方案基于不同的搜索策略,因此它们介绍了搜索器。关于此有足够的评论,因此在这里我不关注它。我的观点是,上面的解决方案会忘记用例-如果您已经参考了用例,那么寻找点是什么呢?在最自然的使用情况下,您还没有针,因此不要使用可变针。
布拉德·威尔逊(Brad Wilson)指出,对象应该承担单一责任。在极端情况下,一个对象只有一个责任而没有状态。然后它就可以成为...一个功能。
needle = findNeedleIn(haystack);
或者您可以这样写:
SynchronizedHaystackSearcherProxyFactory proxyFactory =
SynchronizedHaystackSearcherProxyFactory.getInstance();
StrategyBasedHaystackSearcher searcher =
new BasicStrategyBasedHaystackSearcher(
NeedleSeekingStrategies.getMethodicalInstance());
SynchronizedHaystackSearcherProxy proxy =
proxyFactory.createSynchronizedHaystackSearcherProxy(searcher);
SearchableHaystackAdapter searchableHaystack =
new SearchableHaystackAdapter(haystack);
FindableSearchResultObject foundObject = null;
while (!HaystackSearcherUtil.isNeedleObject(foundObject)) {
try {
foundObject = proxy.find(searchableHaystack);
} catch (GruesomeInjuryException exc) {
returnPitchforkToShed(); // sigh, i hate it when this happens
HaystackSearcherUtil.cleanUp(hay); // XXX fixme not really thread-safe,
// but we can't just leave this mess
HaystackSearcherUtil.cleanup(exc.getGruesomeMess()); // bug 510000884
throw exc; // caller will catch this and get us to a hospital,
// if it's not already too late
}
}
return (Needle) BarnyardObjectProtocolUtil.createSynchronizedFindableSearchResultObjectProxyAdapterUnwrapperForToolInterfaceName(SimpleToolInterfaces.NEEDLE_INTERFACE_NAME).adapt(foundObject.getAdaptable());
haystack.magnet().filter(needle);
代码是在试图找到特定的针头还是只是寻找任何针头?听起来像一个愚蠢的问题,但它改变了这个问题。
寻找特定的针头可以解决问题中的代码。寻找任何针头会更像
needle = haystack.findNeedle()
要么
needle = searcher.findNeedle(haystack)
无论哪种方式,我都希望有一个该类的搜索者。大海捞针不知道如何搜索。从CS的角度来看,它只是您不想要的带有很多废话的数据存储。
另一种可能的方式是为可搜索对象(例如干草堆)和ToBeSearched对象(例如needle)创建两个接口。因此,可以通过这种方式完成
public Interface IToBeSearched
{}
public Interface ISearchable
{
public void Find(IToBeSearched a);
}
Class Needle Implements IToBeSearched
{}
Class Haystack Implements ISearchable
{
public void Find(IToBeSearched needle)
{
//Here goes the original coding of find function
}
}
在大多数情况下,我希望能够对核心对象执行类似这样的简单帮助程序操作,但是根据语言的不同,所讨论的对象可能没有足够或明智的方法。
即使使用JavaScript之类的语言允许您扩展/扩展内置对象的)中,我也发现它既方便又有问题(例如,如果该语言的未来版本引入了一种更有效的方法,而该方法会被自定义方法所取代)。
本文在概述此类方案方面做得很好。
string.join方法)。