我们如何在大海捞针中找到针?

时间:2020-03-05 18:42:19  来源:igfitidea点击:

当以面向对象的方式实现大海捞针的针式搜索时,我们实际上有三种选择:

1. needle.find(haystack)

2. haystack.find(needle)

3. searcher.find(needle, haystack)

你更喜欢哪个?为什么?

我知道有些人喜欢第二种选择,因为它避免引入第三个对象。但是,我不禁感到第三种方法在概念上更"正确",至少在目标是为"真实世界"建模的情况下。

在什么情况下,我们认为引入助手对象(例如本示例中的搜索器)是合理的,何时应避免使用它们?

解决方案

回答

这完全取决于变化和保持不变。

例如,我正在一个(非OOP)框架上,根据针的类型和干草堆的不同,查找算法也不同。除了在面向对象的环境中需要两次调度这一事实之外,这还意味着编写" needle.find(haystack)"或者编写" haystack.find(needle)"都没有意义。

另一方面,应用程序可以很高兴地将查找委托给两个类中的任何一个,或者完全使用一种算法,在这种情况下,决策是任意的。在那种情况下,我更喜欢使用haystack.find(needle)方法,因为将发现应用于干草堆似乎更合乎逻辑。

回答

就个人而言,我喜欢第二种方法。我的理由是,因为我使用的主要API都使用了这种方法,所以我认为这是最有意义的。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

如果我们有东西列表(干草堆),则将搜索(find())针。

回答

我要说的是,选项1已经完全用完了。该代码应该以一种告诉方式进行阅读。选项1使我认为这针将要去找我一个大海捞针。

如果要在干草堆中容纳针叶,则选项2看起来不错。 ListCollections总是将包含ListItems,因此执行collection.find(item)是自然而富有表现力的。

我认为在以下情况下适当地引入辅助对象:

  • 我们不控制问题IE中对象的实现:search.find(ObsecureOSObject,file)
  • IE之间没有常规或者合理的关系:nameMatcher.find(houses,trees.name)

回答

通常应将操作应用于我们要在其上执行的操作...在这种情况下,应该选择大海捞针,因此我认为选项2最合适。

我们还有第四个替代方案,我认为它比替代方案3更好:

haystack.find(needle, searcher)

在这种情况下,它允许我们提供要作为操作一部分进行搜索的方式,因此可以将操作与正在操作的对象一起保留。

回答

还有另一种选择,这是C ++的STL使用的方法:

find(haystack.begin(), haystack.end(), needle)

我认为这是C ++在脸上大喊大叫的一个很好的例子!面向对象。想法是,面向对象编程不是任何形式的灵丹妙药。有时最好用动作来描述事物,有时用对象来描述,有时两者都不是,有时两者都可以。

Bjarne Stroustrup在TC ++ PL中表示,在设计系统时,我们应努力在有效代码的约束下反映现实。对我来说,这意味着我们切勿盲目跟随任何事情。考虑一下即将发生的事情(干草堆,针叶树)以及我们所处的环境(搜索,这就是表达的意思)。

如果重点是搜索,则使用强调搜索的算法(动作)(即可以灵活地适应干草堆,海洋,沙漠,链表)。如果重点是干草堆,则将find方法封装在干草堆对象中,依此类推。

就是说,有时我们会感到疑惑,并且很难做出选择。在这种情况下,应面向对象。如果我们以后改变主意,我认为从对象提取动作然后将动作拆分为对象和类会更容易。

遵循这些准则,代码将更加清晰,美观。

回答

在这三个中,我更喜欢选择#3.

单一责任原则使我不想在我的DTO或者模型上增加搜索功能。他们的责任是成为数据,而不是发现自己,针头也不需要了解干草堆,干草堆也不需了解针头。

对于它的价值,我认为大多数面向对象的从业人员都需要很长时间才能理解为什么#3是最佳选择。在真正摸索之前,我可能做了十年的OO。

@ wilhelmtell,C ++是使模板系统真正起作用的极少数具有模板专业化的语言之一。对于大多数语言,通用的"查找"方法将是一个可怕的想法。

回答

这取决于要求。

例如,如果我们不关心搜索者的属性(例如,搜索者的力量,视野等),那么我会说haystack.find(needle)是最干净的解决方案。

但是,如果我们确实关心搜索器的属性(或者与此有关的任何其他属性),我将向干草堆构造函数或者函数中注入ISearcher接口以简化此操作。这既支持面向对象的设计(大海捞针),又支持控制/依赖注入的倒置(使对"查找"功能进行单元测试更加容易)。

回答

我和布拉德在一起。我在极其复杂的系统上工作的越多,就越需要真正分离对象。他是对的。很明显,一根针对干草堆一无所知,因此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

你明白了。我认为尽可能松散地耦合是一件好事,但也许我有点被迷住了! :)

回答

引用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"能更好地传达程序的含义。

回答

@Peter Meyer
  
  You get the idea. I think going as loosely coupled as possible is a good thing, but maybe I was getting a bit carried away! :)

嗯...是的...我认为IStuffSmallEnoughToBeLostInAHaystack有点危险:-)

回答

You also have a fourth alternative that I think would be better than alternative 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)因为它的简单性更好,所以我愿意为此牺牲一些"正确性"。

回答

When implementing a needle search of a
  haystack in an object-oriented way,
  you essentially have three
  alternatives:
  
  
  needle.find(haystack)
  haystack.find(needle)
  searcher.find(needle, haystack)
  
  
  Which do you prefer, and why?

如果我错了,请指正我,但是在所有三个示例中,我们已经参考了要寻找的针,所以这不是像当我们坐在鼻子上时寻找眼镜一样吗? :p

撇开双关语,我认为这实际上取决于我们认为大海捞针在给定范围内的责任。我们是否只是在关心包含针的东西(本质上是一个集合)?然后haystack.find(needlePredicate)很好。否则,farmBoy.find(predicate,haystack)可能更合适。

回答

haystack.magnet().filter(needle);

回答

简单:烧干草堆!之后,仅保留针头。另外,我们可以尝试磁铁。

一个更棘手的问题:如何在针头中找到一根特定的针头?

答案:对每一个线程进行线程处理,并将线程的另一端添加到已排序的索引(即指针)上

回答

大海捞针不应该知道针,大海捞针不应该知道大海捞针。搜索者需要了解两者,但是,大海捞针是否应该知道如何进行搜索本身才是争用的重点。

所以我将混合使用2和3. 干草堆应该能够告诉其他人如何进行搜索,而搜索者应该能够使用该信息来搜索干草堆。

回答

class Haystack {//whatever
 };
class Needle {//whatever
 }:
class Searcher {
   virtual void find() = 0;
};

class HaystackSearcher::public Searcher {
public:
   HaystackSearcher(Haystack, object)
   virtual void find();
};

Haystack H;
Needle N;
HaystackSearcher HS(H, N);
HS.find();

回答

确实是2和3的混合。

一些干草堆没有专门的搜索策略;一个例子是数组。找到某些东西的唯一方法是从头开始并测试每个项目,直到找到所需的项目为止。

对于这种情况,自由函数可能是最好的(例如C ++)。

一些干草堆可以对它们施加专门的搜索策略。可以对数组进行排序,例如,允许我们使用二进制搜索。一个自由函数(或者一对自由函数,例如sort和binary_search)可能是最好的选择。

一些草垛将集成的搜索策略作为其实施的一部分;例如,关联容器(散列或者有序集合和映射)都可以。在这种情况下,查找可能是必不可少的查找方法,因此它可能应该是一种方法,即haystack.find(needle)。

回答

我可以想到前两种口味中的任何一种都有意义的情况:

  • 如果需要对针进行预处理,例如在Knuth-Morris-Pratt算法中,则needle.findIn(haystack)(或者pattern.findIn(text))很有意义,因为needle对象保存了为该对象创建的中间表该算法有效地工作
  • 如果干草堆需要进行预处理(例如在trie中说),则haystack.find(needle)(或者words.hasAWordWithPrefix(prefix))效果更好。

在以上两种情况下,针头或者干草堆之一都知道搜索。此外,他们俩彼此了解。如果我们不希望针头和干草堆彼此之间或者搜索时相互了解,则使用searcher.find(needle,haystack)是合适的。

回答

代码是在试图找到特定的针头还是只是寻找任何针头?听起来像一个愚蠢的问题,但它改变了这个问题。

寻找特定的针头问题中的代码是有意义的。寻找任何针头会更像

needle = haystack.findNeedle()

或者

needle = searcher.findNeedle(haystack)

无论哪种方式,我都希望有一个该类的搜索者。大海捞针不知道如何搜索。从CS的角度来看,它只是我们不想要的带有很多废话的数据存储。

回答

如果Needle和Haystack都是DAO,则选项1和2都不可行。

这样做的原因是,DAO仅应负责保存它们正在建模的现实世界对象的属性,并且仅具有getter和setter方法(或者仅具有直接属性访问权限)。这使得将DAO序列化到文件,或者为通用比较/通用副本创建方法更容易编写,因为代码不会包含一堆" if"语句来跳过这些辅助方法。

这只剩下选项3,大多数人都同意这是正确的行为。

选项3具有一些优点,最大的优点是单元测试。这是因为现在可以轻松地模拟Needle和Haystack对象,而如果使用选项1或者2,则必须先修改Needle或者Haystack的内部状态,然后才能执行搜索。

其次,现在将搜索者放在一个单独的类中,可以将所有搜索代码(包括通用搜索代码)保存在一个地方。而如果将搜索代码放入DAO中,则常见的搜索代码或者存储在复杂的类层次结构中,或者存储在Searcher Helper类中。

回答

干草堆可以包含东西
一种类型的东西是针
finder是负责搜索内容的东西
取景器可以接受一堆东西作为在哪里找到东西的来源

查找器还可以接受需要查找的事物的事物描述

因此,最好是,对于灵活的解决方案,我们可以执行以下操作:
IStuff界面

干草堆= IList <IStuff>
针头:IStuff

发现者
.Find(IStuff stuffToLookFor,IList <IStuff> stuffsToLookIn)

在这种情况下,解决方案将不仅仅局限于针和干草堆,而是可用于实现该接口的任何类型

因此,如果我们想在海洋中找到一条鱼,就可以。

回答

var结果= Finder.Find(鱼类,海洋)

  • 空间
  • 稻草
  • 搜寻者

该问题的答案实际上应取决于解决方案所针对的领域。
如果我们碰巧在物理草垛中模拟物理搜索,则可能有此类

空间
知道哪些对象位于哪个坐标
遵守自然法则(转换能量,检测碰撞等)

针,稻草
位于太空
对部队作出反应

搜寻者
与空间互动:
动手,施加磁场,烧干草,施加X射线,寻找针头...

因此,seeker.find(针,空间,策略)或者seeker.find(针,空间,策略)

大海捞针恰好在我们要寻找针头的空间中。当我们将空间抽象为一种虚拟机(想像为矩阵)时,我们可以使用干草堆代替空间来获得以上内容(解决方案3 / 3b):

seeker.find(针,干草堆,策略)或者seeker.find(针,干草堆,策略)

但是矩阵是域,如果针头不能放在其他任何地方,则只能用干草堆代替。

  • 制作能够找到自己的针头,例如他们经常问自己:我迷路了吗?如果答案是肯定的,则他们将GPS计算的位置发送给某人或者开始发出哔哔声或者其他声音:" needle.find(space)"或者" needle.find(haystack)"(解决方案1)
  • 在每个稻草上安装带有照相机的干草堆,之后我们可以询问干草堆配置单元是否最近看过针头:haystack.find(needle)(解决方案2)
  • 将RFID标签贴到针上,这样就可以轻松地对它们进行三角剖分

再说一次,那只是一种动物。有趣的是,这为全新的方向打开了思路:
1.为什么首先要松开针头?我们可以更改流程,所以不会放弃它吗?
2.我们是否必须找到丢失的针头,还是可以简单地再买一根针而忘记第一根呢? (如果针头过一会儿溶解,那就很好了)
3.如果我们定期松动针头并且需要再次找到它们,那么我们可能想要

就是说,在实现中,我们已经在某种程度上制造了针,干草堆以及大多数情况下的矩阵。

  • 大海捞针的目的是为了大海捞针吗?然后寻求解决方案2.
  • 针在任何地方丢失是否自然?然后寻求解决方案1.
  • 针头是否会意外丢入大海捞针?然后寻求解决方案3. (或者考虑其他恢复策略)

回答

因此,请根据域来决定:

haystack.find(needle),但应该有一个搜索器字段。

我知道依赖注入非常流行,所以@Mike Stone的haystack.find(needle,searcher)被接受并不奇怪。但我不同意:在我看来,选择使用哪种搜索器是大海捞针的决定。

考虑两个ISearcher:MagneticSearcher遍历整个体积,以与磁体强度一致的方式移动和步进磁体。 QuickSortSearcher将堆栈分为两部分,直到其中一个子堆中的针明显为止。搜索者的正确选择可能取决于干草堆的大小(例如相对于磁场),针如何进入干草堆(即针的位置是真正随机的还是偏斜的?)等。

回答

如果我们有haystack.find(needle,searcher),那么意思是"最好的选择是在干草堆之外进行的。"我认为这可能不正确。我认为"干草堆知道如何最好地自我搜索"的可能性更大。添加设置器,如果我们需要覆盖或者进行测试,仍然可以手动注入搜索器。

Needle needle = (Needle)haystack.searchByName("needle");

如果我们有对针头物体的参考,为什么要搜索它? :)
问题域和用例告诉我们,不需要在大海捞针中的确切位置(就像从list.indexOf(element)可以得到的一样),我们只需要一根针即可。而且我们还没有。所以我的答案是这样的

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);

或者

回答

我同意,有更多可能的解决方案基于不同的搜索策略,因此它们介绍了搜索器。对此有足够的评论,因此在这里我不关注它。我的观点是,上面的解决方案会忘记用例,如果我们已经参考过用例,那么寻找点是什么呢?
在最自然的使用情况下,我们还没有针,因此不要使用可变针。

绝对是第三,恕我直言。

大海捞针的问题就是试图在大量其他物体中找到一个物体的一个例子,这表明它将需要一个复杂的搜索算法(可能涉及磁铁或者(更可能的)子过程),而它并没有期望大海捞针进行线程管理或者实现复杂的搜索是很有意义的。

回答

但是,搜索对象专用于搜索,并且可以期望知道如何管理子线程以进行快速的二进制搜索,或者使用搜索到的元素的属性来缩小范围(即:寻找铁质物品的磁铁) 。

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

   }

}

回答

另一种可能的方式可以是为Searchable对象创建两个接口,例如干草堆和ToBeSearched对象,例如针。
因此,可以通过这种方式完成

needle = findNeedleIn(haystack);

布拉德·威尔逊(Brad Wilson)指出,对象应该承担单一责任。在极端情况下,一个对象只有一个责任而没有任何状态。然后它可以成为...一个功能。

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());

段落数量不匹配