我一直想知道在编码之前我是在考虑太多还是太少。如果我不确定未来可能需要更改哪些我需要考虑的情况,对我来说尤其如此。我不知道我应该如何灵活或抽象地制作课程。我举一个简单的例子。
你想编写一个针对计算机玩二十一点的程序,你就是那种喜欢尝试的人。你开始编写套牌的代码,但是你知道二十一点可以有1,2,4或任意数量的套牌。你说明了这一点,但是你意识到也许甲板会被改变而没有价值十的牌。然后,您决定该套牌应该是完全多功能的,以允许任何数量的套装或等级。然后你决定甲板的规则应该能够从标准的套装数量乘以独特的等级改变到等于套牌中的牌总数......你可以看到我要去的地方。
我的问题是,是否有关于课程应该如何灵活的指导方针?
答案 0 :(得分:5)
支持极简主义和封装,避免你不需要的功能。
根据需要进行设计当然是好的,但是应该尽量减少使用不使用的东西 - 或者将来可能 使用的东西混乱的设计。可以考虑并实施您确定需要的东西。
当您理解并指定“未来问题”(特别是将来的那个问题)时,您将经常解决它与当今解决方案的不同。
答案 1 :(得分:5)
查看David Parnas撰写的精彩论文“On the Criteria to be Used in Decomposing Systems into Modules”,后面是 1972 。
一般来说,您应该尝试确定责任区域,这可以推到隐藏有用功能和复杂性的非常简单的界面背后。您应该努力将您认为最有可能改变的区域中的内容与分开(即预测变化)。
答案 2 :(得分:2)
灵活性确实是应用程序/系统可维护的要求。通常我发现遵循SOLID设计原则,TDD和无状态业务逻辑的设计更容易维护。
在所有SOLID原则中,我发现[S] RP是使应用程序可维护的规则。在[S] RP之后,您的系统将被分解为更小的部分,具有可替换的类。假设它可以分解为Deck
,DeckRule
,HitAction
等。
界面或继承会有所帮助,因为您可以轻松地将Deck
与NoTenDeck
或SpadeOnlyDeck
进行交换。您可以将DeckRule
交换为HardToWinDeckRule
或ImpossibleWinDeckRule
。使用decorator
或其他设计模式(例如composite
)也有助于提高系统的灵活性。不要忘记测试单元,它将帮助您重构代码。
有时你需要像Breaking Change
这样的东西,你需要拆除你当前的架构和界面,用另一种设计来代替。有时它需要,但大部分都不需要。
您可以在stackoverflow answer for DI vs Singleton and little about state or stateless找到几个讨论。
答案 3 :(得分:1)
我尝试遵循YAGNI的敏捷原则! - 你不需要它。
提出所有这些可能的未来要求是不值得的。未来可能有无限的要求。你无法解释所有这些问题。只需做你需要做的就可以满足你已经拥有的要求。
如果您将来获得新要求,那么请更改系统。 (你确实有很好的测试,以确保你在重构期间不会破坏任何东西吗?)
答案 4 :(得分:1)
关于灵活性的整体想法
根据您对问题的描述,我不认为您的课程应该灵活,因为“继续将游戏规则的每个新方面都投入到同一个班级”。一个责任太多的阶级很脆弱,难以维护,因而难以改变 - 具有讽刺意味的是,将一个阶级当作灵活处理最终会使它变得僵化!不要将所有鸡蛋放在一个篮子里。单独的关注意味着单独的阶级。您的整体设计应该灵活,而不是您的课程。
关于二十一点问题
纸牌游戏,特别是复杂和/或不断变化的游戏,通常是最特殊的动物,因此在尝试提高设计技能时可能不是一个很好的标准示例。
如果你想要真正的模块化,你可能需要一个可插拔的规则引擎,它允许插件在游戏的不同阶段挂钩,让你可以访问相关的资源来改变从分数到事件序列的任何内容。甚至其他规则。
我对此的看法是
"I ain't gonna need this rule/type of hook into the game"
使用YAGNI而不是"I ain't gonna need a rules engine"
,因为你知道你必须拥有一个。{/ li>
或者,
您可能还想查看https://gamedev.stackexchange.com/有关游戏的设计指南。
答案 5 :(得分:0)
在编写代码时,我倾向于寻找可能在以后明显添加的内容。 “明显可能是一个词来定义。:-)这意味着你确定将来会发布的内容。除此之外,我尽量不用担心。