Questions tagged «design»

有关通过软件设计解决问题和计划解决方案的问题。

11
设计理想范围文字
我一直在思考如果要设计一种语言,我将如何设计“完美”范围文字。对于您不知道的语句中的范围文字,它表示一个值范围,例如1-4。它们最常用于for / foreach循环 似乎应该考虑几个问题 支持包含范围和排除范围,将+1或-1附加到端点似乎有点笨拙且容易出错。 支持步进,因此您可以设置例如偶数或奇数范围 可读性,范围文字描述的内容应该显而易见 明确,范围文字所描述的内容应该完全明确 默认值可能是从包含值到排除值,因为在大多数情况下,这是用于遍历数组等的方式。 无论如何,我所见过的范围文字的一个示例是Ruby,其形式为1..3表示排他性(最后),1 ... 3表示排他性(最后)。您也可以执行1..10.step(5)。经过仔细考虑后,我发现该方法有一些我不喜欢的地方(从我对红宝石的有限了解) 您只能在最后描述包含式和排除式。在描述大多数情况时,它似乎并不一致。 仅需额外增加一个。似乎是使难以看清范围是包含范围还是排除范围的秘诀。我不了解你,但是点变得模糊起来了:) 为范围添加表示法之类的方法似乎将文字的概念与类的概念混合在一起,这似乎有点不一致(即使将范围编译为一个类) 无论如何,在考虑其他选择之后。我想出了这个 [5..1] 5,4,3,2,1 [1..5 [ 1,2,3,4 ] 1..5] 2,3,4,5 [ 0..5..20] 0,5,10,15,20 等等。我喜欢它,因为[通常会否定一个集合,即使这样与集合形成对比,它也很适合。 我有点担心的一件事是使排他性/包含性指示符是否强制性,即,如果只写1..5,则默认值为1,2,3,4,因为这是数组等的最常见情况。 。它更容易阅读,但更具针对性,如果您不得不写[1..5 [您会早日了解它们的工作原理。 那么,您认为我涵盖了大多数基础,却忽略了某些内容吗?您会否使[]为强制性?您会在编程语言中设计范围文字吗? 候选人 括号样式:[0..10 [ ,带有步骤: [0..5..20 [ 间隔符号:[0..10) ,步长:[0..5..20) 感叹为排他。0 ..!10,含步骤:0..5 ..!20 不同的步骤。0 ..!20,5 但是,这将使默认值* 0..10'包含在内 罗:[0至!20 x 5] …

9
与YAGNI相比,删除硬编码的值和防御性设计
首先介绍一下背景。我正在从“年龄->费率”中查找代码。有7个年龄段,因此查找表是3列(From | To | Rate)和7行。这些值很少更改-它们是已保持3年不变的立法费率(第一列和第三列)。我认为存储该表而不对其进行硬编码的最简单方法是在数据库中的全局配置表中,因为包含CSV的单个文本值(因此“ 65,69,0.05,70,74,0.06” 65-69和70-74层)。比较容易解析然后使用。 然后,我意识到要实现这一点,我将必须创建一个新表,一个要包装它的存储库,用于仓库的数据层测试,围绕将CSV展开到表中的代码的单元测试以及围绕查找本身的测试。所有这些工作的唯一好处是避免对查找表进行硬编码。 与用户交谈时(当前他们直接使用查找表-通过查看纸质副本)时,人们几乎认为“费率永远不会改变”。显然,这实际上是不正确的-费率是三年前才创建的,过去,“永不改变”的习惯已经改变了-因此,对于我来说,应该进行防御性编程,我绝对不应该在其中存储查找表应用程序。 除了我想YAGNI的时候。我要实现的功能未指定费率会改变。如果费率确实发生变化,它们仍将变化得很少,以至于甚至不需要考虑维护,并且该功能实际上并不十分重要,以至于如果在费率变化和更新的应用程序之间存在延迟,任何事情都将受到影响。 我几乎已经决定,如果我对查询进行硬编码,就不会失去任何价值,而且我不太担心我对这一特定功能的处理方式。我的问题是,作为专业人士,我是否可以合理地证明这一决定?硬编码值是不好的设计,但是麻烦从应用程序中删除值似乎违反了YAGNI原则。 编辑为了澄清这个问题,我不关心实际的实现。我担心自己可能会做出快速,糟糕的事情并通过说出YAGNI来证明其合理性,或者我会采取一种防御性更高的努力方法,即使在最好的情况下,最终收益也很小。作为一名专业程序员,我实施我知道有缺陷的设计的决定是否仅仅取决于成本/收益分析? 编辑虽然所有的答案都非常有趣,因为我认为这归结为个人的设计选择,我认为最好的答案是@科尔宾的和@EZ哈特为他们带来了的事情,我没有在这个问题考虑: 错误的二分法是通过将其移动到数据库来“正确删除硬编码的值”,而不是使用硬编码来“有效地应用YAGNI”。第三种选择是将查找表放入应用程序配置中,这不会产生正确方法的开销,而且没有YAGNI的效率。我们通常不限于任何一个或一个决定,然后可以归结为成本/收益决定。 代码生成可以减少将硬编码值移动到数据库的开销,并且还可以消除我过度设计的决定,即将CSV处理到表中的决定。如果查询方法的基本要求发生变化,则可能还会对生成的代码造成长期维护问题。这一切都只会影响成本/收益分析,而且如果我拥有自动化功能,那么我什至都不会考虑对这种东西进行硬编码。 我将@Corbin的答案标记为正确,因为它改变了我对开发成本的假设,并且我可能会在不久的将来向我的军械库中添加一些代码生成工具。
10 design 

6
当Dijkstra写关于关注点分离的文章时,他是否打算进行代码模块化?
首先,我阅读了摘自Edsger W. Dijkstra 1974年的论文“关于科学思想的作用”: 让我尝试向您解释,我的品味是所有明智思维的特征。就是说,一个人出于自己的一致性而愿意独立地深入研究其主题的一个方面,一直都知道一个人只在其中一个方面中占据一席之地。我们知道一个程序必须是正确的,我们只能从那个角度研究它;我们也知道它应该是有效的,可以这么说,我们可以在另一天研究它的效率。在另一种情况下,我们可能会问自己,是否这样做:为什么,该程序是理想的。但是,通过同时处理这些各个方面,则无济于事!这就是我有时所说的“关注点分离”,即使不是完全可能,据我所知,这是有效地整理思想的唯一可用技术。这就是我所说的“将注意力集中在某个方面”的意思:这并不意味着忽略其他方面,这只是正视这个事实,即从该方面的观点来看,另一方面是无关紧要的。它同时是一轨和多轨。 我看到关注点的现代分离正在谈论将代码模块化。但是,阅读上面的引用后,我理解这是一次将您的注意力集中在一项特定的任务上,而不是将精力集中在其他方面。在我看来,这并不意味着必须将代码分成模块化的块。 也就是说,在您面前有一个代码,即在一个文件中的所有文件都包含视图,存储库,控制器,事件处理,工厂等概念。 作为一个简短的示例,下面是一些具有数据访问权限并查看(输出)的代码: $sql = "SELECT * FROM product WHERE id = " . db_input($id); $row = db_fetch_array(db_query($sql)); <option value="<?=$row['id']?>"<?= $row['ver'] == $row['ver'] ? ' selected="selected"' : '' ?>>Version <?=$row['ver']?></option> 使用现代的OO,我可以使用Repository模式将数据访问放在其自己的文件中,View代码可以进入其自己的文件模板,并且可以将它们连接在一起以通过控制器(或Action或Request Handler)进行通信,并且我可以添加一个工厂来创建和连接各种依赖项。我可以有一个定义这些工厂的配置文件。当然,这与单一文件的一切相去甚远。 我关于关注点分离的问题是这样的:阅读Dijkstra的报价,我有一个想法,即他不一定意味着关注点分离是“代码的模块化分离(到文件或它们自己的函数/方法/等)中”,而且他的意思还在于让人们将注意力集中在程序的某个方面,而不用专心于其他重要但目前尚未考虑的方面,而无论它们是否在代码中物理上分开。 那么为什么我们要负担物理模块化代码分离和设计模式呢?不管代码的结构如何,仅将精力集中在一个方面是否足够? 我不是在谈论编写最可怕的意大利面条代码,然后只考虑它的一个方面,这很可能是一种负担。但是最后,我要解决的是,当不需要在精神上专注于某个方面时,为什么要执行物理代码分离,为什么要将代码拆分为分离的文件或块(方法)? 关注点分离应该仍然是一种心理锻炼,而不是身体锻炼吗? 换句话说,编程的精神(专注)和物理(纸上代码)方面是否应该脱节?

2
异常的粒度
我和几个朋友之间进行了辩论。他们更喜欢一般性例外,例如作为例外的字段ClientErrorException并ServerErrorException有详细信息,而我更喜欢使事情更具体。例如,我可能有一些例外,例如: BadRequestException AuthenticationFailureException ProductNotFoundException 这些都是基于API返回的错误代码构建的。 遵循异常的优点,这对于Java来说似乎是惯用的。但是,我朋友的意见并不少见。 就代码可读性和API可用性而言,是否存在首选方法,或者它真的只是偏爱首选项?

7
我是否使我的课程过于细腻?单一责任原则应如何应用?
我编写了许多涉及三个基本步骤的代码。 从某处获取数据。 转换数据。 将该数据放在某处。 我通常最终使用三种类型的类-受它们各自的设计模式的启发。 工厂-从某些资源构建对象。 调解员-要使用工厂,执行转换,然后使用指挥官。 指挥官-将数据放在其他地方。 我的班级往往很小,通常是一个(公共)方法,例如获取数据,转换数据,执行工作,保存数据。这导致类的激增,但总体上效果很好。 我在进行测试时遇到的困难是,我最终会紧密耦合测试。例如; 工厂-从磁盘读取文件。 指挥官-将文件写入磁盘。 没有其他人,我无法测试。我可以编写其他“测试”代码来进行磁盘读/写操作,但是随后我要重复一遍。 看看.Net,File类采用了不同的方法,它将(我的)工厂和指挥官的职责结合在一起。它具有“创建”,“删除”,“存在”和“全部读取”功能。 我是否应该遵循.Net的示例并将我的类结合在一起(尤其是在处理外部资源时)?它仍然耦合了代码,但是它更具故意性-它发生在原始实现中,而不是在测试中。 我在这里是否过于狂热地应用了“单一责任原则”?我有负责读和写的单独的类。当我可以拥有一个负责处理特定资源(例如系统磁盘)的组合类时。

2
这是C ++中基于“ pImpl”的类层次结构的好方法吗?
我有一个我希望将接口与实现分开的类层次结构。我的解决方案是有两个层次结构:接口的句柄类层次结构和实现的非公共类层次结构。基本句柄类具有一个指向实现的指针,派生的句柄类将其强制转换为派生类型的指针(请参见function getPimpl())。 这是我对带有两个派生类的基类的解决方案的草图。有更好的解决方案吗? 文件“ Base.h”: #include <memory> class Base { protected: class Impl; std::shared_ptr<Impl> pImpl; Base(Impl* pImpl) : pImpl{pImpl} {}; ... }; class Derived_1 final : public Base { protected: class Impl; inline Derived_1* getPimpl() const noexcept { return reinterpret_cast<Impl*>(pImpl.get()); } public: Derived_1(...); void func_1(...) const; ... }; class Derived_2 …
9 design  c++  c++11 

3
接口依赖于具体类是否可以?
我在Java中为自定义错误处理程序创建接口。 想要传递一个参数错误对象,但我需要它成为Exception类的子对象。 在接口中使用我定义的类名可以吗? 就不依赖于任何实现而言,这是否会使它减少接口的数量? 我尝试做这样的事情: public class CustomException { /* ... Implementation ... */ } public interface Interface { void onError(CustomException ex); }

1
代码设计:委托任意函数
在PPCG上,我们经常遇到“山丘之王”挑战,这使不同的代码bot相互竞争。我们不希望将这些挑战限制为单一语言,因此我们通过标准I / O进行跨平台通信。 我的目标是编写一个框架,使挑战编写者可以使用它来简化编写这些挑战的过程。我想满足以下要求: 挑战编写者可以创建一个类,其中方法表示每种不同的通信。例如,在我们的《善与恶》挑战中,作者将创建一个Player具有abstract boolean vote(List<List<Boolean>> history)方法的类。 当调用上述方法时,控制器能够提供通过标准I / O进行通信的上述类的实例。也就是说,并非上述类的所有实例都必须通过标准I / O进行通信。其中的3个机器人可能是本机Java机器人(简单地覆盖了Player该类,另外2个机器人则使用另一种语言) 这些方法将不会总是具有相同数量的参数(也不会总是具有返回值) 我希望挑战编写者必须尽可能少地工作才能使用我的框架。 我不反对使用反射来解决这些问题。我考虑过要求挑战编写者做类似的事情: class PlayerComm extends Player { private Communicator communicator; public PlayerComm(Communicator communicator){ this.communicator = communicator; } @Override boolean vote(List<List<Boolean>> history){ return (Boolean)communicator.sendMessage(history); } } 但是如果有几种方法,这可能会很重复,而且持续进行强制转换并不有趣。(sendMessage在此示例中,将接受可变数量的Object参数,并返回Object) 有一个更好的方法吗?

2
编程到面向数据的接口
我们的代码库的一部分以以下样式编写: // IScheduledTask.cs public interface IScheduledTask { string TaskName { get; set; } int TaskPriority { get; set; } List<IScheduledTask> Subtasks { get; set; } // ... several more properties in this vein } // ScheduledTaskImpl.cs public class ScheduledTaskImpl : IScheduledTask { public string TaskName { get; set; } public …

2
接口隔离原理:如果接口存在大量重叠该怎么办?
摘自敏捷软件开发,原理,模式和实践:Pearson新国际版: 有时,由不同客户端组调用的方法会重叠。如果重叠很小,则组的接口应保持分离。公用功能应在所有重叠的接口中声明。服务器类将从每个接口继承通用功能,但仅实现一次。 鲍伯叔叔,谈论有微小重叠的情况。 如果出现大量重叠该怎么办? 说我们有 Class UiInterface1; Class UiInterface2; Class UiInterface3; Class UiIterface : public UiInterface1, public UiInterface2, public UiInterface3{}; 如果UiInterface1和之间有大量重叠,该UiInterface2怎么办?

2
为什么使用UML图计划代码的组织方式不合适?
因此,是的,图表有时可能不合适。什么时候不合适?当您创建没有代码的代码来验证它们时,然后打算遵循它们。绘制图表探索想法没有错。 敏捷软件开发:原理,模式和实践 -Robert C. Martin 他到底是什么意思?UML是否旨在帮助计划如何在 “切入” 之前构造代码?如果您不遵循自己想到的图,使用它有什么意义? 上下文:在本章中,鲍伯叔叔为保龄球比赛的得分保持者制作了一个UML图。然后,他继续以测试驱动的方式开发程序,而无需查阅UML图。生成的程序与UML图完全不同,鲍勃叔叔得出了上面引用的结论。


3
CRUD API:如何指定要更新的字段?
假设您有某种数据结构,该数据结构保留在某种数据库中。为简单起见,我们将此数据结构称为Person。现在,您要负责设计CRUD API,该API允许其他应用程序创建,读取,更新和删除Person。为简单起见,让我们假定通过某种Web服务访问此API。 对于CRUD的C,R和D部分,设计很简单。我将使用类似C#的功能符号-实现可以是SOAP,REST / JSON或其他方式: class Person { string Name; DateTime? DateOfBirth; ... } Identifier CreatePerson(Person); Person GetPerson(Identifier); void DeletePerson(Identifier); 那更新呢?自然要做的是 void UpdatePerson(Identifier, Person); 但你会如何指定哪些领域Person要更新? 我可以提出的解决方案: 您始终可以要求通过一个完整的“人员”,即客户将执行以下操作来更新出生日期: p = GetPerson(id); p.DateOfBirth = ...; UpdatePerson(id, p); 但是,这将需要某种事务上的一致性或在Get和Update之间锁定;否则,您可能会覆盖其他客户端并行进行的其他更改。这会使API更加复杂。此外,由于下面的伪代码(假设客户端语言支持JSON),因此容易出错。 UpdatePerson(id, { "DateOfBirth": "2015-01-01" }); - 看起来正确-不仅会更改DateOfBirth,而且会将所有其他字段重置为null。 您可以忽略所有的字段null。但是,您将如何在不更改 DateOfBirth和有意将其更改为null之间做出区别? 将签名更改为void UpdatePerson(Identifier, Person, ListOfFieldNamesToUpdate)。 将签名更改为void …

1
C ++序列化设计评论
我正在编写一个C ++应用程序。大多数应用程序都需要读写数据引用,这一点也不例外。我为数据模型和序列化逻辑创建了一个高级设计。这个问题要求考虑以下特定目标对我的设计进行审查: 以一种简单灵活的方式来读取和写入任意格式的数据模型:原始二进制,XML,JSON等。等 数据格式应与数据本身以及请求序列化的代码分离。 为了确保序列化尽可能地没有错误。I / O具有多种固有的风险:我的设计是否引入了更多的失败方法?如果是这样,我如何重构设计以减轻这些风险? 该项目使用C ++。无论您是喜欢还是讨厌它,语言都有其自己的处理方式,并且设计旨在与该语言一起工作,而不是反对它。 最后,该项目基于wxWidgets构建。当我在寻找适用于更一般情况的解决方案时,此特定实现应与该工具箱配合良好。 接下来是用C ++编写的一组非常简单的类,它们说明了设计。这些不是我到目前为止已经部分编写的实际类,此代码仅说明了我正在使用的设计。 首先,一些示例DAO: #include <iostream> #include <map> #include <memory> #include <string> #include <vector> // One widget represents one record in the application. class Widget { public: using id_type = int; private: id_type id; }; // Container for widgets. Much more …
9 design  c++  c++11 

2
我们需要验证整个模块的使用情况还是仅验证公共方法的参数?
我听说建议您验证公共方法的参数: 如果他不期望为空,是否应该检查为空? 方法是否应验证其参数? MSDN-CA1062:验证公共方法的参数(我具有.NET背景,但问题不是特定于C#的) 动机是可以理解的。如果将以错误的方式使用模块,则我们希望立即引发异常,而不是任何不可预测的行为。 令我困扰的是,错误的参数并不是使用模块时唯一的错误。这是一些错误情况,如果我们遵循建议并且不希望错误升级,则需要添加检查逻辑: 来电-意外参数 来电-模块处于错误状态 外部通话-返回意外结果 外部调用-意外的副作用(两次进入调用模块,破坏了其他依赖状态) 我试图考虑所有这些情况,并用一种​​方法(对不起,不是C#的人)编写一个简单的模块: public sealed class Room { private readonly IDoorFactory _doorFactory; private bool _entered; private IDoor _door; public Room(IDoorFactory doorFactory) { if (doorFactory == null) throw new ArgumentNullException("doorFactory"); _doorFactory = doorFactory; } public void Open() { if (_door != null) throw …

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.