有一句名言
程序代码然后获取信息 做出决定。面向对象的代码 告诉对象做事。 - 亚历克 尖锐
该帖子的主题正是关于此。
我们假设我们正在开发一款游戏,其中Game
存在Board
。
当面临决定我们将在Board
课程中实施哪些方法的问题时,我总是想到两种不同的方式:
使用Board
,getSize()
,getPieceAt(x, y)
填充setPieceAt(x, y, piece)
课程。这似乎是合理的,并且通常在库/框架中找到。 Board
类具有一组想要共享的内部功能,并具有一组方法,允许类的客户端按照自己的意愿控制类。客户应该询问他需要的东西并决定做什么。如果他想将所有棋盘片设置为黑色,他将“手动”迭代它们以实现该目标。
寻找Board
的依赖类,看看他们“告诉”它要做什么。 ClassA
想要计算有多少件是红色的,所以我要实现一个calculateNumberOfRedPieces()
。 ClassB
打算清除Board
上的所有内容(例如,将所有内容设置为NullPiece
),因此我会向{clearBoard()
方法添加Board
方法1}}类。这种方法不太通用,但在其他方面允许更多的灵活性。如果我“隐藏”Board
在IBoard
界面后面,并且决定我想要一个无限大小的棋盘,那么第一种方式,我会被卡住,就像我一样必须遍历无限数量的项目!另一方面,通过这种方式,我可以做得很好(例如,我可以假设除哈希表中包含的所有部分都是空的!)。
我知道如果我打算创建一个库,我可能会坚持第一种方法,因为它更通用。另一方面,当我完全控制将使用Board
类的系统时,我想知道要采用哪种方法 - 当我还要设计时所有将使用Board
的类。目前,以及将来(如果后来我决定添加依赖于Board
具有不同“欲望”的新类,第二种方法是否会引发问题?)。
答案 0 :(得分:4)
引用实际上是警告您远离那些对它们所持有的数据不做任何事情的数据结构。因此,第一种方法中的Board类可能会被一般的集合所取代并取而代之。
无论如何,Single Responsibility Principle仍然适用,因此您需要谨慎对待第二种方法。
我要做的是调用YAGNI(你不需要它)并试着看看我可以使用泛型集合而不是Board类。如果你发现以后你确实需要董事会,那么它的责任可能会更加清晰。
答案 1 :(得分:1)
让我提出逆向观点。我认为第二种方法有腿。我同意单一责任原则,但在我看来,Board
课程有一个可辩护的单一任务/关注点:保持竞争环境。
我可以设想一组非常合理的方法,例如getSize()
,getPiece(x,y)
,setPiece(x, y, color)
,removePiece(x, y)
,movePiece(x1,y1,x2,y2)
,clear()
, countPieces(color)
,listPiecePositions(color)
,read(filename)
,write(filename)
等具有明确共识的明确共同任务。以抽象方式处理这些董事会管理问题将允许其他类更干净地实现游戏逻辑,并且将来Board
或Game
更容易扩展。
YAGNI一切都很好,但我的理解是,它促使你不要开始建造美丽的建筑物,希望有一天它们会被有效占用。例如,我不会花费任何时间来研究无限的游戏表面,3D游戏表面或可以嵌入球体的游戏表面的未来可能性。如果我非常认真地对待YAGNI,在需要之前我不会写出简单明了的Board
方法。
但这并不意味着我会将董事会作为一个概念性组织或可能的阶级而弃权。这当然并不意味着我根本不会考虑如何分离我的计划中的问题。在我的世界中,至少YAGNI不需要从最低级别的数据结构开始,通过封装很少或没有,以及完全程序化的方法。
我不同意这样的观点:第一种方法更为笼统(以任何有用的方式),或者在共识中看来应该“只看到在没有抽象任何东西的情况下你能走多远”。老实说,这听起来像我们解决eight queens的方式。 1983年。在Pascal。
YAGNI是一个伟大的指导原则,有助于避免很多second system effect和类似的自下而上,我们可以做我们应该犯的错误。但是YAGNI越过Agile Practice Stupidity Threshold是而不是的美德。
答案 2 :(得分:0)
CurtainDog是对的,调用Yagni并找出你现在真正需要的东西,实现它,然后确保它不会阻止将来可能需要的任何功能。
第二种方法违反了超类不应该知道每个子类的原则。我认为您缺少的元素是基类可以定义模板方法,如getBoardSize,countRedPieces,countBlackPieces,可以被子类覆盖,而您的超类具有使用这些模板方法的代码,因此告诉它的子类要做什么,但不是怎么做。