Questions tagged «design-patterns»

设计模式是解决软件设计中常见问题的通用可重用解决方案。

5
我们可以使用策略模式和依赖注入完全取代继承吗?
例如: var duckBehaviors = new Duckbehavior(); duckBehaviors.quackBehavior = new Quack(); duckBehaviors.flyBehavior = new FlyWithWings(); Duck mallardDuck = new Duck(DuckTypes.MallardDuck, duckBehaviors) 由于Duck类包含所有行为(抽象),因此似乎不需要创建新类MallardDuck(扩展了Duck)。 参考:Head First设计模式,第1章。

6
依赖注入的最佳定义是什么?
每次有人联系我,并要求我以概念性方式定义“依赖项注入”,并解释在软件设计中使用DI的真正利弊。我承认我很难解释DI的概念。每次我需要告诉他们有关单一责任原则,组成而不是继承的历史时。 任何人都可以帮助我解释为开发人员描述DI的最佳方法吗?

4
什么是“过早抽象”?
我听过这个短语被扔掉了,对我来说,论点听起来完全是疯了(对不起,如果我在这里是稻草人,那不是我的意图),通常它遵循以下原则: 您不希望在知道一般情况之前先创建一个抽象,否则(1)您可能会将不属于您的东西放在抽象中,或者(2)忽略了重要的事情。 (1)对我来说,这听起来像程序员不够务实,他们已经假设事情会出现在最终程序中,而事实并非如此,因此他们使用的抽象程度很低,问题不是过早的抽象,它是过早的凝结。 (2)忽略重要的事情是一回事,完全有可能在规范中省略了某些事情,后来证明这很重要,解决这个问题的方法不是在发现自己时就浪费自己的资源和浪费资源。猜错了,这是从客户端获取更多信息。 我们应该始终从抽象到具体工作,因为这是最实用的做事方式,而不是相反。 如果我们不这样做,那么我们可能会误解客户,并创造需要更改的东西,但是如果我们仅构建客户以其自己的语言定义的抽象,那么我们就永远不会遇到这种风险(至少远不及承担风险)是在黑暗中带着某种凝结的镜头),是的,客户可能会改变对细节的想法,但是他们最初用来传达他们想要的内容的抽象往往仍然有效。 这是一个示例,假设客户希望您创建一个物品装箱机器人: public abstract class BaggingRobot() { private Collection<Item> items; public abstract void bag(Item item); } 我们正在从客户端使用的抽象中构建一些东西,而没有涉及我们不知道的事情的更多细节。这是非常灵活的,我已经看到这被称为“过早抽象”,而实际上假设套袋是如何实施还为时过早,可以说与客户讨论后,他们希望一次将多个袋装成袋。为了更新班级,我只需要更改签名,但是对于自下而上的人可能需要进行大范围的系统检修。 没有过早的抽象,只有过早的凝结。这句话有什么问题?我的推理的缺陷在哪里?谢谢。

2
警报系统架构
我想创建一个系统,处理来自各种程序的警报消息,并可以通过电子邮件将这些警报处理给顺风顺水的消费者。所有这些都将包含在一个内部网络中。 我想我希望基本架构看起来像这样: 我目前主要关心的是“消息处理程序”位,这就是我的“ API排序”。我希望该系统的所有组件将数据发送到API,该API处理所有对数据库的写入。我认为这种方法比较容易,因为它简化了安全性,并允许我将许多更复杂的数据库查询包含在一个程序中。 令人担心的是,我希望这与语言无关,这意味着任何代码都应能够将消息发送到我的处理程序,由它来解释它们。我希望通过JSON平面文件-或通过对该程序的REST调用(为下游应用程序提供灵活性)来做到这一点。 我的问题是 我应该烦恼消息处理程序吗?或者它会增加简单性,使其仅允许直接数据库访问下游应用程序以及其他两个组件(管理控制台和警报管理器)? 这样,他们就可以插入想要的任何警报-只要将INSERT插入数据库表中即可。 我不是专业的软件设计师,所以请原谅-我只想在业余时间做一个项目。

6
如何避免在次优设计中无休止地迭代?
因此,可能像许多人一样,我经常发现自己对设计问题感到头疼,例如,有些设计模式/方法似乎可以直观地解决问题并具有预期的好处。通常会有一些警告,如果没有某种工作就很难实施模式/方法,而这周围的工作否定了模式/方法的好处。我可以很轻松地结束许多模式/方法的迭代,因为可以预见的是,几乎所有的模式/方法在现实世界中都存在一些非常重要的警告,在这些情况下,根本没有一个简单的解决方案。 例: 我将给您一个假设的示例,大致基于我最近遇到的一个真实示例。假设我想在继承上使用合成,因为过去继承层次结构阻碍了代码的可伸缩性。我可能会重构代码,但随后发现在某些上下文中,超类/基类只是需要在子类上调用功能,尽管尝试避免这样做。 下一个最好的方法似乎是实现一半委托/观察者模式和一半组合模式,以便超类可以委托行为,或者子类可以观察超类事件。然后,该类的可伸缩性和可维护性较差,因为它不清楚应如何扩展,而且扩展现有的侦听器/代理也很棘手。同样,信息也隐藏得不好,因为人们开始需要了解实现以了解如何扩展超类(除非您非常广泛地使用注释)。 因此,在此之后,我可能会选择完全使用观察者或委托人来避免因混淆大量方法而带来的弊端。但是,这有其自身的问题。例如,我可能会发现我最终需要观察者或代表来应对越来越多的行为,直到我几乎需要针对每种行为的观察者/代表。一种选择可能是只为所有行为提供一个大的侦听器/代理,但是实现类最终会带有许多空方法等。 然后,我可以尝试另一种方法,但是与此同时存在很多问题。然后是下一个,然后是下一个,依此类推。 当每种方法似乎都存在其他问题时,这种迭代过程将非常困难,并导致某种设计决策瘫痪。无论使用哪种设计模式或方法,都很难接受代码最终同样会带来问题。如果我最终陷入这种情况,是否意味着问题本身需要重新考虑?别人遇到这种情况时会做什么? 编辑: 似乎有一些我想清除的问题的解释: 我将OOP完全排除在问题之外,因为事实证明它实际上并不是特定于OOP的,而且很容易误解我在传递OOP时所做的一些评论。 有些人声称我应该采取迭代的方法并尝试不同的模式,或者当它停止工作时我应该放弃一个模式。这是我首先要参考的过程。我认为这个例子很清楚,但是我可以更清楚一点,所以我编辑了问题。

4
编译器如何从类型错误中恢复?
我已经阅读了几篇论文,文章和《编译器:原理,技巧和工具(第二版)》(第2版)(又称“龙书”)第4章第4.1.4节,它们都讨论了语法编译器错误恢复的主题。但是,在尝试了几种现代编译器之后,我发现它们还可以从语义错误以及语法错误中恢复。 我非常了解编译器从句法相关的错误中恢复的算法和技术,但是我并不完全理解编译器如何从语义错误中恢复。 我目前使用的访问者模式略有变化,以从我的抽象语法树生成代码。考虑我的编译器编译以下表达式: 1 / (2 * (3 + "4")) 编译器将生成以下抽象语法树: op(/) | ------- / \ int(1) op(*) | ------- / \ int(2) op(+) | ------- / \ int(3) str(4) 然后,代码生成阶段将使用访问者模式来递归地遍历抽象语法树并执行类型检查。将遍历抽象语法树,直到编译器到达表达式的最内层为止。(3 + "4")。然后,编译器会检查表达式的每一面,并发现它们在语义上不相等。编译器引发类型错误。这就是问题所在。编译器现在应该做什么? 为了使编译器能够从此错误中恢复并继续对表达式的外部部分进行类型检查,它必须从评估表达式的最内部部分到表达式的下一个内部部分返回某种类型(int或str)。但是它根本没有返回的类型。由于发生类型错误,因此没有推断出任何类型。 我提出的一种可能的解决方案是,如果确实发生类型错误,则应该引发错误,并且应该将一个特殊值(表示发生类型错误)返回给以前的抽象语法树遍历调用。如果先前的遍历调用遇到此值,则他们知道在抽象语法树中更深处发生类型错误,因此应避免尝试推断类型。尽管此方法确实有效,但效率似乎很低。如果表达式的最内层部分位于抽象语法树的深处,则编译器将不得不进行许多递归调用,而仅是意识到无法完成任何实际工作,而只需从每个返回即可。 我是否使用了上述方法(我对此表示怀疑)。如果是这样,效率不高吗?如果不是,那么编译器从语义错误中恢复时将使用什么方法?

5
违反LSP可以吗?
我正在跟进这个问题,但是我将重点从代码转换为原则。 根据我对Liskov替换原理(LSP)的理解,无论我的基类中有什么方法,它们都必须在我的子类中实现,并且根据此页面,如果您在基类中重写了一个方法,则它什么也不做或抛出一个错误。例外,您违反了该原则。 现在,我的问题可以这样总结:我有一个abstract Weapon class,两个类,Sword和Reloadable。如果Reloadable包含一个特定的method,称为Reload(),我将不得不向下访问以访问该method,并且理想情况下,您希望避免这种情况。 然后,我想到了使用Strategy Pattern。这样,每把武器都只知道它能够执行的动作,因此,例如,一种Reloadable武器显然可以重新装弹,但是Sword不能,甚至不知道Reload class/method。正如我在Stack Overflow帖子中所说的那样,我不必沮丧,也可以维护List<Weapon>收藏集。 在另一个论坛上,第一个答案建议让其Sword意识到Reload,只是什么也不要做。我在上面链接到的“堆栈溢出”页面上也给出了相同的答案。 我不完全明白为什么。为什么要违反该原则并让Sword知道Reload并保留为空?正如我在Stack Overflow帖子中所说,SP几乎解决了我的问题。 为什么它不是可行的解决方案? public final Weapon{ private final String name; private final int damage; private final List<AttackStrategy> validactions; private final List<Actions> standardActions; private Weapon(String name, int damage, List<AttackStrategy> standardActions, List<Actions> attacks) { this.name = name; this.damage = damage; standardActions = …

5
对相似的功能使用不同的模式
我是该项目的唯一开发人员,就像其他软件项目一样,将来可能会被其他人采用。 假设我使用模式X来实现功能A。在开发和完成功能之后,我意识到可以使用我刚刚学习的模式Y来实现相同的功能。但是功能A运行良好,并且从X到Y的重构既耗时又无济于事。 然后是实现功能B的时候了。它与A相似,但是这次我想借此机会使用模式Y。我对最终结果感到满意,比使用功能A更好,但是现在我的代码使用了两个具有相似特征的X和Y不同的图案。 但是没有真正的理由使用不同的模式,除了以下事实:构建功能AI的技能不足以使用与功能B相同的模式。 请注意,此问题不是针对给定问题选择正确的模式;这是在代码库中共存的两种模式,用于解决类似的问题,如果有足够的时间进行重构,则可以将其减少为一种。 该代码闻到了吗? 保持这样的源代码有什么缺点? 我应该只使用一种模式吗?即重构A以使用Y或在编写B时继续使用X? 从源头上,我该如何传达出针对相似功能存在两种不同模式的原因基本上是没有原因的? 我是否担心下一位开发人员对我的代码的看法?

4
Java:如何实现设置程序的顺序无关紧要的步骤生成器?
编辑:我想指出,这个问题描述了一个理论上的问题,并且我知道我可以使用构造函数参数作为强制参数,或者如果API使用不正确则抛出运行时异常。然而,我在寻找,它的解决方案不要求构造函数参数或运行时检查。 假设您有一个Car这样的界面: public interface Car { public Engine getEngine(); // required public Transmission getTransmission(); // required public Stereo getStereo(); // optional } 正如评论所暗示的,a Car必须具有Engineand,Transmission而a Stereo是可选的。这意味着可以build()将Car实例创建的Builder 仅应在已将和都指定给该Builder实例的情况下才具有build()方法。这样,类型检查器将拒绝编译任何尝试在不使用or 的情况下创建实例的代码。EngineTransmissionCarEngineTransmission 这需要一个步骤构建器。通常,您将实现以下内容: public interface Car { public Engine getEngine(); // required public Transmission getTransmission(); // required public Stereo getStereo(); // optional public class Builder …

3
使用接口进行松耦合代码
背景 我有一个项目,该项目取决于某种类型的硬件设备的使用情况,但是只要该硬件设备执行我需要的操作,则由谁来制造该硬件设备并不重要。话虽这么说,即使两个本来应该做同样事情的设备,如果不是由同一家制造商生产的,它们也会有差异。因此,我正在考虑使用一个接口将应用程序与所涉及设备的特定制造商/型号分离开,而使该接口仅涵盖最高级别的功能。这就是我在想我的体系结构的外观: 在一个C#项目中定义一个接口IDevice。 在另一个C#项目中定义的库中有一个具体的东西,它将用来表示设备。 有具体的设备实现IDevice接口。 该IDevice接口可能具有类似GetMeasurement或的方法SetRange。 使应用程序了解具体的知识,并将具体的知识传递给利用(而非实现)IDevice设备的应用程序代码。 我非常确定这是正确的做法,因为这样我就可以在不影响应用程序的情况下更改正在使用的设备(这似乎有时会发生)。换句话说,具体的实现方式GetMeasurement或SetRange实际操作方式并不重要(设备制造商之间可能会有所不同)。 我唯一的疑问是,现在应用程序和设备的具体类都依赖于包含IDevice接口的库。但是,这是一件坏事吗? 我也看不到应用程序不需要知道设备的方式,除非设备与设备IDevice位于相同的名称空间中。 题 这似乎是实现接口以解耦应用程序与所使用设备之间的依赖关系的正确方法吗?

5
使用编译时值参数生成Java类
考虑一个类实现相同的基本行为,方法等的情况,但是该类可能存在多种不同版本以用于不同用途。在我的特定情况下,我有一个向量(一个几何向量,而不是一个列表),并且该向量可以应用于任何N维欧几里德空间(1维,2维等)。如何定义此类/类型? 在C ++中,这很容易,因为C ++中的类模板可以将实际值作为参数,但是在Java中我们没有那么奢侈。 我可以想到的解决该问题的两种方法是: 在编译时实现每种可能的情况。 public interface Vector { public double magnitude(); } public class Vector1 implements Vector { public final double x; public Vector1(double x) { this.x = x; } @Override public double magnitude() { return x; } public double getX() { return x; } } public class …

3
避免构造函数有很多参数
所以我有一个工厂,可以创建不同类的对象。可能的类都是从抽象祖先派生的。工厂具有配置文件(JSON语法),并根据用户的配置来决定要创建哪个类。 为此,工厂使用boost :: property_tree进行JSON解析。他遍历ptree并决定要创建哪个具体对象。 但是,产品对象具有许多字段(属性)。根据具体的类,对象具有大约5-10个属性,将来可能会更多。 因此,我不确定对象的构造函数应如何。我可以想到两种解决方案: 1)产品的构造函数希望将每个属性作为参数,因此构造函数最终将带有10个以上的参数。这将是丑陋的,并导致较长的,不可读的代码行。但是,优点是工厂可以解析JSON并使用正确的参数调用构造函数。产品类不需要知道由于JSON配置而已创建。不需要知道完全涉及JSON或配置。 2)产品的构造函数只需要一个参数property_tree对象。然后,它可以解析所需的信息。如果配置中的信息丢失或超出范围,则每个产品类别都可以正确响应。工厂不需要知道几种产品需要哪些参数。如果配置错误,工厂也不需要知道如何应对。而且构造函数的接口是统一的,很小的。但是,缺点是,该产品需要从JSON中提取所需的信息,因此,它知道如何构造它。 我倾向于选择解决方案2)。但是,我不确定这是否是好的工厂模式。让产品知道它是用JSON配置创建的,这感觉有点不对劲。另一方面,可以很简单地介绍新产品。 有什么意见吗?

3
是否存在用于管理深层多对多关系的设计模式?
我在定义这个数据模式时遇到了麻烦,在多个应用程序上工作时遇到了麻烦。 它包括: 由许多对象本身组成的对象类型 第二种对象类型,其中每个实例“具有很多”第一个对象 并且,每个对象的每个关联都可以将第一对象的每个子对象修改为第二对象类型。 一个简单的示例可能是: 一门编程课程,包含一组课程 这些课程由一组作业组成。 可以将课程分配给学生。 但是,一旦将课程分配给学生,则可以通过删除和添加为该学生定制每个课程和/或作业,以至于原始课程可能无法识别。 在我的解决方案中,结果是: 将课程分配给学生后,该课程将加载到内存中。然后,对于每个子对象,使用适当的元数据生成学生/子对象关系对象。本质上,我使用原始对象作为模板来生成所需的可自定义对象。 随着子对象变得更加复杂和编号,这将导致大量数据。我想知道是否存在一些优化或模式,以减少处理此数据模式所需的逻辑/复杂度。

4
减少类的复杂性
我已经查看了一些答案并在Google上进行了搜索,但是找不到任何有用的东西(即不会产生尴尬的副作用)。 总的来说,我的问题是我有一个对象,需要对它执行长时间的操作。我将其视为一种装配线,例如制造汽车。 我相信这些对象将称为“ 方法对象”。 因此,在此示例中的某个点上,我将拥有一个CarWithoutUpholstery,然后需要在其上运行installBackSeat,installFrontSeat,installWoodenInserts(这些操作不会互相干扰,甚至可以并行执行)。这些操作由CarWithoutUpholstery.worker()执行,并产生一个新的对象,该对象将是CarWithUpholstery,然后在其上运行cleanInsides(),verifyNoUpholsteryDefects()等。 单个阶段中的操作已经是独立的,也就是说,我已经在努力应对可以按任何顺序执行的子集(可以按任何顺序安装前排和后排座椅)。 我的逻辑当前使用反射来简化实现。 也就是说,一旦有了CarWithoutUpholstery,该对象就会检查自身是否有名为performSomething()的方法。此时,它执行所有这些方法: myObject.perform001SomeOperation(); myObject.perform002SomeOtherOperation(); ... 同时检查错误和内容。虽然操作的顺序是不重要的,我已经在指定情况下,我曾经发现毕竟一些顺序很重要,一个字典顺序。这与YAGNI矛盾,但是它花费很少-简单的sort()-并且可以节省大量的方法重命名(或引入其他执行测试的方法,例如方法数组)的费用。 一个不同的例子 让我们说,除了要制造汽车外,我还必须编辑某人的秘密警察报告,并将其提交给我的邪恶霸主。我的最后一个对象将是ReadyReport。为了构建它,我首先收集基本信息(姓名,姓氏,配偶...)。这是我的A阶段。根据是否有配偶,我可能不得不进入B1或B2阶段,并收集一两个人的性行为数据。这是由对不同的Evil Minions的几个不同的查询所组成,它们控制着夜生活,街拍,性用品商店的销售收据以及不收什么。等等等等。 如果受害者没有家庭,我什至不会进入GetInformationAboutFamily阶段,但是如果我这样做了,那么我是首先瞄准父亲还是母亲还是兄弟姐妹(如果有)就没有关系了。但是,如果我还没有执行FamilyStatusCheck,那我就无法做到这一点,因此它属于早期阶段。 一切都很棒... 如果我需要一些其他操作,我只需要添加一个私有方法, 如果该操作在多个阶段都是通用的,那么我可以让它从超类继承, 操作简单且自成体系。任何其他操作都不需要从任何操作中获得任何价值(在不同阶段执行的操作), 线下的对象不需要执行许多测试,因为如果其创建者对象首先没有验证这些条件,它们甚至可能不存在。即,在将插入物放入仪表板,清洁仪表板并验证仪表板时,我不需要验证仪表板是否确实存在。 它便于测试。我可以轻松模拟部分对象并在其上运行任何方法,并且所有操作都是确定性的黑匣子。 ...但... 当我在我的方法对象之一中添加最后一个操作时,就会出现问题,这导致整个模块超过了强制性的复杂性指标(“少于N个私有方法”)。 我已经在楼上解决了这个问题,并建议在这种情况下,私有方法的丰富并不是灾难的故事。复杂性是存在的,但它的存在,因为操作是复杂的,实际上它不是那么复杂-它只是长。 以邪恶的霸王为例,我的问题是邪恶的霸王(又名不容否认的他)要求了所有饮食信息,我的饮食奴才告诉我,我需要查询餐馆,小厨房,街头小贩,无牌街头小贩,温室所有者等,以及邪恶的(sub)Overlord(众所周知的也不应被拒绝的他)抱怨我在GetDietaryInformation阶段执行了太多查询。 注意:我知道从多个角度来看这根本不是问题(忽略可能的性能问题等)。发生的一切是,一个特定的指标不满意,对此有充分的理由。 什么我想我能做到 除了第一个以外,所有这些选择都是可行的,而且我认为是可以辩护的。 我已经证实自己可以偷偷摸摸,并声明一半的方法protected。但是我会利用测试程序中的弱点,除了在被抓住时证明自己是合理的之外,我也不喜欢这样。另外,这是权宜之计。如果所需操作次数加倍怎么办?不太可能,但是那又如何呢? 我可以将此阶段任意拆分为AnnealedObjectAlpha,AnnealedObjectBravo和AnnealedObjectCharlie,并使每个阶段要执行的操作占三分之一。我的印象是,这实际上增加了复杂性(增加了 N-1个类),除了通过测试之外没有任何好处。我当然可以认为,CarWithFrontSeatsInstalled和CarWithAllSeatsInstall在逻辑上是连续的阶段。Alpha后来要求使用Bravo方法的风险很小,如果我打得很好的话,风险甚至会更低。但是还是。 我可以将一个远程相似的不同操作组合在一起。performAllSeatsInstallation()。这只是权宜之计,并且确实增加了单个操作的复杂性。如果我需要以不同的顺序执行操作A和B,并且将它们打包在E =(A + C)和F(B + D)内,则必须解开E和F并重新整理代码。 我可以使用lambda函数数组,并完全避开检查,但是我发现这很笨拙。但是,这是迄今为止最好的选择。它会摆脱反思。我遇到的两个问题是,可能会要求我重写所有方法对象,不仅是假设对象,CarWithEngineInstalled而且这虽然是很好的工作安全性,但实际上并没有那么大的吸引力。并且代码覆盖率检查器存在lambda 问题(可以解决,但仍然存在)。 所以... 您认为哪个是我最好的选择? 有没有我没有考虑过的更好的方法?(也许我最好打扫卫生,直接问一下是什么?) 这种设计是否存在绝望的缺陷,我最好承认失败与否-这种体系结构?这对我的职业生涯不利,但是从长远来看,编写错误的代码会更好吗? 我当前的选择是否真的是“唯一的方式”,我需要努力获得更好的质量指标(和/或仪器)?对于这最后一个选项,我需要参考...我不能只在抱怨的同时对@PHB挥手,这不是您要查找的指标。不管我想要多少

2
使用包(宝石,鸡蛋等)创建解耦的架构
主要问题 眼看着良好的支持最先进的编程平台有包管理(想想gem,npm,pip,等),它是有意义的设计应用程序或系统由内部开发的软件包,从而促进并创建一个松散耦合的架构? 例 这样的一个示例是创建用于数据库访问以及用于身份验证和系统其他组件的软件包。当然,这些也使用外部程序包。然后,您的系统导入并使用这些软件包-而不是将其代码包含在自己的代码库中。 注意事项 对我来说,这似乎可以促进代码去耦并提高可维护性,几乎是一种基于Web的桌面应用程序方式(更新几乎是自动应用的,单一功能的单一代码库,等等)。 这看起来像是一个合理而理智的设计概念吗?如今,这实际上已用作构建应用程序的标准方法吗?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.