你如何在大海捞针中find针?
当以面向对象的方式执行大海捞针search时,您基本上有三种select:
1. needle.find(haystack) 2. haystack.find(needle) 3. searcher.find(needle, haystack)
你更偏向于哪个,为什么?
我知道有些人更喜欢第二种select,因为它避免了引入第三个对象。 然而,我不禁感觉到,第三种方法在概念上更“正确”,至less你的目标是build模“现实世界”。
在这种情况下,您认为引入帮助对象(如本例中的search者)是合理的,何时应该避免?
通常行动应该适用于你正在做的行动…在这种情况下,干草堆,所以我认为选项2是最合适的。
你也有第四个替代scheme,我认为会比替代scheme3更好:
haystack.find(needle, searcher)
在这种情况下,它允许您提供您想要作为操作的一部分进行search的方式,因此您可以保留正在操作的对象的操作。
在三个,我更喜欢选项#3。
单一责任原则使我不想把searchfunction放在我的DTO或模型上。 他们的责任是成为数据,而不是发现自己,也不应该知道大海捞针,大海捞针知道针。
对于它的价值,我认为大多数面向对象的从业者需要很长时间才能理解为什么#3是最好的select。 我在十年前做了OO,可能之前我真的很喜欢它。
@wilhelmtell,C ++是模板专业化的less数语言之一,使这样一个系统实际上工作。 对于大多数语言来说,一个通用的“查找”方法将是一个可怕的想法。
还有另一种方法,就是C ++的STL使用的方法:
find(haystack.begin(), haystack.end(), needle)
我认为这是一个很好的例子C ++高呼“在你的脸上!” 到OOP。 这个想法是OOP不是任何种类的银弹, 有时候,事情最好用行动来描述,有时用对象来表示,有时候既不是,有时也是两者。
Bjarne Stroustrup在TC ++ PL中表示,当你devise一个系统时,你应该努力在有效和高效的代码的约束下反映现实。 对我而言,这意味着你不应该盲目追随任何事物。 想想手头的事情(干草堆,针头)和我们所处的环境(search,这就是expression的意思)。
如果重点是关于search,那么使用强调search(即灵活地适合干草堆,海洋,沙漠,链表)的algorithm(动作)。 如果重点是关于干草堆,那么将find方法封装在干草堆对象内,等等。
也就是说,有时候你会有疑问,并且很难做出select。 在这种情况下,应该是面向对象的。 如果稍后改变主意,我认为从对象中提取动作然后将动作拆分为对象和类是比较容易的。
遵循这些准则,你的代码将会更清晰,更美好。
我会说选项1是完全没有的。 代码应该以一种告诉你它做什么的方式读取。 选项1让我觉得这个针头要去找我一个干草堆。
select2看起来不错,如果干草堆是为了包含针。 ListCollections总是包含ListItems,所以做collection.find(item)是自然的和expression的。
我认为在以下情况下引入辅助对象是合适的:
- 你不能控制有问题的对象的实现
IE:search.find(ObsecureOSObject,文件) - 对象之间没有规律或合理的关系
IE:nameMatcher.find(房屋,trees.name)
我和布拉德在一起。 我在越来越复杂的系统上工作的越多,就越需要真正的解耦对象。 他是对的。 很明显,一根针不应该知道干草堆的事情,所以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应该只负责保存他们正在build模的真实世界对象的属性,并且只有getter和setter方法(或者只是直接访问属性)。 这使得将DAO序列化为一个文件,或者为通用比较/通用拷贝创build方法更容易编写,因为代码不会包含大量的“if”语句来跳过这些辅助方法。
这只是留下了选项3,大多数人会认同这是正确的行为。
选项3有几个优点,最大的优点是unit testing。 这是因为针和干草堆的物体现在都可以很容易地被模拟出来,而如果使用了选项1或2,在进行search之前,针或干草堆的内部状态将不得不被修改。
其次,现在search者在一个单独的类中,所有的search代码都可以保存在一个地方,包括常见的search代码。 而如果search代码被放到DAO中,常见的search代码将被存储在一个复杂的类层次结构中,或者通过一个search帮助器类来存储。
当以面向对象的方式执行大海捞针search时,您基本上有三种select:
needle.find(干草堆)
haystack.find(针)
searcher.find(针,干草堆)
你更偏向于哪个,为什么?
纠正我,如果我错了,但在所有三个例子中,你已经有一个你正在寻找的针头的参考,所以当他们坐在你的鼻子上,这不是有点像寻找你的眼镜吗? :p
在旁边,我认为这取决于你认为干草堆在特定领域的责任。 我们只关心它是否包含针(一个集合,本质上)? 然后haystack.find(needlePredicate)很好。 否则,farmBoy.find(predicate,haystack)可能更合适。
这完全取决于什么变化和保持不变。
例如,我正在研究一个(非OOP) 框架 ,其中查找algorithm根据针的types和干草堆的不同而不同。 除了需要在面向对象的环境下进行双重调度的事实之外,这也意味着编写needle.find(haystack)
或者写haystack.find(needle)
是没有意义的。
另一方面,您的应用程序可以愉快地将查找委托给两个类中的任一个,或者完全使用一个algorithm,在这种情况下,决定是任意的。 在这种情况下,我更喜欢haystack.find(needle)
方式,因为把这个发现应用到干草堆似乎更合乎逻辑。
这取决于你的要求。
例如,如果你不关心search者的属性(例如search者的强度,视觉等),那么我会说haystack.find(针)将是最干净的解决scheme。
但是,如果你关心search者的属性(或者任何其他属性),我会将ISearcher接口注入干草堆构造函数或函数中,以便于实现。 这支持面向对象的devise(干草堆有针)和控制/dependency injection的反转(使unit testing“查找”function更容易)。
引用SICP的伟大作者,
程序必须是为人们阅读而写的,而且只是偶然地让机器执行
我更喜欢方法1和方法2。 以ruby为例,它带有.include?
这是这样使用的
haystack.include? needle => returns true if the haystack includes the needle
有时候,纯粹出于可读性的原因,我想翻转一下。 Ruby不带有in?
方法,但它是一个单线,所以我经常这样做:
needle.in? haystack => exactly the same as above
如果强调大海捞针或search操作“更重要”,我更愿意写“ include?
。 通常,干草堆或者search都不是你所关心的东西,只是物体存在 – 在这种情况下,我发现in?
更好地传达节目的意义。
我可以想到前两种口味中的任何一种都有意义的情况:
-
如果针需要预处理,像Knuth-Morris-Prattalgorithm一样,
needle.findIn(haystack)
(或pattern.findIn(text)
)是有意义的,因为针对needle.findIn(haystack)
持有为algorithm创build的中间表有效 -
如果haystack需要预处理,比如说在trie中,
haystack.find(needle)
(或者words.hasAWordWithPrefix(prefix)
)会更好。
在上述两种情况下,针或干草堆中的一种都知道search。 而且,他们都知道对方。 如果你想让针和干草堆不要察觉对方或search,那么searcher.find(needle, haystack)
就是合适的。
简单:烧干草堆! 之后,只剩下针头。 另外,你可以尝试磁铁。
一个更难的问题是:如何在针池中find一个特定的针头?
答案:线程每一个线程的每个链的另一端连接到一个有序的索引(即指针)
这个问题的答案实际上应该取决于解决scheme实施的领域。
如果你碰巧模拟了身体干草堆的物理search,你可能会有类
- 空间
- 稻草
- 针
- 导引头
空间
知道哪些对象位于哪个坐标
实现自然规律(转换能量,检测碰撞等)
针 , 稻草
位于空间
对部队作出反应
导引头
与空间相互作用:
移动手,施加磁场,烧干草,应用X射线,寻找针 …
因此, seeker.find(needle, space)
或者seeker.find(needle, space, strategy)
干草堆正好在你要找针的地方。 当你把空间抽象为一种虚拟机时(想想:matrix),你可以用干草堆而不是空间来获得上述结果(解决scheme3 / 3b) :
seeker.find(needle, haystack)
或seeker.find(needle, haystack, strategy)
但matrix是域,如果你的针不能在其他地方,只能用干草堆取而代之。
然后,这又是一个神学。 有趣的是,这打开了全新的方向:
1.你为什么首先放松针头? 你可以改变这个过程,所以你不会放松吗?
2.你必须find丢失的针头,或者你可以简单地find另一个,忘记第一个? (如果一段时间后针头溶解了,那就太好了)
3.如果你经常放松针头,又需要再find它们,那么你可能会想
-
使针能够find自己,例如,他们经常问自己:我输了吗? 如果答案是肯定的,他们会将他们的GPS计算位置发送给某人,或者开始发出哔哔声:
needle.find(space)
或needle.find(haystack)
(解决scheme1) -
在每根稻草上安装一个带有摄像头的干草堆,然后你可以问最近看到针的大海捞针:
haystack.find(针) (解决scheme2) -
把RFID标签贴在你的针上,这样你就可以很容易地对它们进行三angular测量
这只是说,在你的实施中, 你制造了针和干草堆,大部分时间是在某种程度上的matrix。
所以根据你的域名决定:
- 干草堆的目的是包含针吗? 然后去解决scheme2。
- 针是否在任何地方丢失是自然的? 然后去解决scheme1。
- 指针是否因意外而迷失在干草堆中? 然后去解决scheme3(或考虑另一个恢复策略)
haystack.find(针),但应该有一个search字段。
我知道dependency injection是非常愤怒的,所以@Mike Stone的haystack.find(needle,searcher)已经被接受了,这并不令我感到惊讶。 但我不同意:select什么样的search者,似乎是为了干草堆的决定。
考虑两个ISearchers:MagneticSearcher在音量上迭代,以与磁体强度一致的方式移动和移动磁体。 QuickSortSearcher将堆栈分成两部分,直到针在其中一个子堆中明显。 search者的正确select可能取决于干草堆的大小(例如相对于磁场),针进入干草堆的方式(即,针的位置是真正的随机的还是偏向的)等等。
如果你有haystack.find(针,search者),你会说“select哪一个是最好的search策略最好是在大海捞针之外完成”。 我不认为这可能是正确的。 我认为“干草堆知道如何最好地search自己”的可能性更大。 添加一个setter,如果你需要重写或testing,你仍然可以手动注入search器。
我个人喜欢第二种方法。 我的推理是因为我所使用的主要API使用这种方法,而且我觉得这是最有意义的。
如果你有一个清单(干草堆),你会search(find())的针。
彼得·迈耶
你明白了。 我认为尽可能松散耦合是一件好事,但也许我有点被带走了! 🙂
错误…是的…我认为IStuffSmallEnoughToBeLostInAHaystack
类是一个红旗:-)
你也有第四个替代scheme,我认为会比替代scheme3更好:
haystack.find(needle, searcher)
我明白你的观点,但是如果searcher
实现了一个search界面,可以search除干草堆以外的其他types的对象,还可以find除针之外的其他东西呢?
界面也可以用不同的algorithm来实现,例如:
binary_searcher.find(needle, haystack) vision_searcher.find(pitchfork, haystack) brute_force_searcher.find(money, wallet)
但是,正如其他人已经指出的,我也认为这只有在你有多个searchalgorithm或多个可search或可find的类时才有用。 如果不是的话,我同意haystack.find(needle)
因其简单性而更好,所以我愿意牺牲一些“正确性”。
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的混合,真的。
有些干草堆没有专门的search策略; 这是一个数组。 find某种东西的唯一方法就是从一开始就开始testing每个项目,直到find你想要的。
对于这样的事情,一个免费的function可能是最好的(如C ++)。
一些干草堆可以有一个专门的search策略强加给他们。 例如,一个数组可以被sorting,允许你使用二进制search。 一个免费的函数(或者一对免费的函数,比如sort和binary_search)可能是最好的select。
一些干草堆有一个完整的search策略作为其实施的一部分; 例如,关联容器(散列或有序集合和映射)都可以。 在这种情况下,查找可能是一个重要的查找方法,所以它可能应该是一个方法,即haystack.find(针)。
干草堆可以包含的东西一种types的东西是查找器是负责search的东西发现者可以接受一堆东西作为find东西发现者的来源也可以接受东西的东西,它需要find
所以,最好为了一个灵活的解决scheme,你会做这样的事情:IStuff接口
Haystack = IList <IStuff> Needle:IStuff
Finder.Find(IStuff stuffToLookFor,IList <IStuff> stuffsToLookIn)
在这种情况下,您的解决scheme将不会被束缚在针和干草堆上,但它可用于实现接口的任何types
所以如果你想在海洋中find一条鱼,你可以。
var results = Finder.Find(fish,ocean)
如果你有一个针对象的参考,你为什么要search它? :)问题的领域和用例告诉你,你不需要在大海捞针的确切位置(就像你可以从list.indexOf(元素)),你只需要一个针。 而你还没有呢 所以我的答案是这样的
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);
我同意有更多的可能的解决scheme是基于不同的search策略,所以他们介绍search。 对此有很多评论,所以我不在这里注意。 我的观点是上面忘记使用案例的解决scheme – 如果你已经有了参考,那么寻找什么东西是重点? 在最自然的使用情况下,你还没有针,所以你不使用可变的针。
布莱德·威尔逊指出,对象应该有一个单一的责任。 在极端情况下,一个对象有一个责任,没有一个状态。 那么它可以成为一个function。
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());
干草堆不应该知道针,而针不应该知道干草堆。 search者需要了解这两者,但是干草堆是否应该知道如何search自己是争论的真正点。
所以我会select2和3的混合 干草堆应该能够告诉别人如何search,而search者应该能够使用这些信息来search干草堆。
haystack.magnet().filter(needle);
代码试图find一个特定的针或只是任何针? 这听起来像一个愚蠢的问题,但它改变了问题。
寻找一个特定的针,问题中的代码是有道理的。 寻找任何针它会更像
needle = haystack.findNeedle()
要么
needle = searcher.findNeedle(haystack)
无论哪种方式,我更喜欢有一个类的search者。 干草堆不知道如何search。 从CS的angular度来看,它只是一个数据存储,你不想要的废话很多。
绝对是第三,恕我直言。
大海捞针的问题就是试图在大量的其他对象中find一个对象的例子,这表明它需要一个复杂的searchalgorithm(可能涉及到磁铁或者更可能是子过程),而且它不会“对于希望进行线程pipe理或执行复杂search的干草堆而言,这是非常有意义的。
然而,search对象专用于search,并且可以期望知道如何pipe理用于快速二分search的子线程,或者使用search到的元素的属性来缩小该区域(即:find铁项目的磁铁) 。
另一种可能的方式是创build两个可search对象的接口,例如haystack和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 } }
haystack.iterator.findFirst(/* pass here a predicate returning true if its argument is a needle that we want */)
iterator
可以接口到任何不可变的集合,集合有共同的findFirst(fun: T => Boolean)
方法来完成这个工作。 只要干草堆是不变的,不需要从“外部”隐藏任何有用的数据。 当然,把自定义的非平凡集合和其他一些有haystack
东西联系在一起并不好。 分而治之,好吗?