【问题标题】:OO Design, open/closed principle questionOO 设计,开放/封闭原则问题
【发布时间】:2010-09-10 14:33:27
【问题描述】:

我一直在思考这个面向对象的设计问题,但无法提出令人满意的解决方案,所以我想在这里向人群开放以征求意见。

我有一个代表回合制棋盘游戏的 Game 类,就这个问题而言,我们可以假设它类似于 Monopoly。 在我的设计中,我有一个 Player 类,其中包含一个方法 TakeTurn

Game 循环遍历所有 Player 并调用 TakeTurn 方法来完成所有必要的事情以完成转弯。 我希望能够拥有 n 个玩家,并且能够将任意数量的玩家设置为电脑玩家。 所以,我的想法是有一个 HumanPlayer 类和一个 ComputerPlayer 类,它们都派生自 Player。

Game 只知道 Player 类,并简单地依次在每个 Player 上调用 TakeTurn 方法。 我的问题在于 ComputerPlayer 对象可以完全自动化,即与 Monopoly 示例保持一致,可以决定使用某些逻辑购买房产。 现在,使用 HumanPlayer 对象,它需要从实际用户那里获得输入才能购买房产,例如,这似乎暗示了不同的界面,并且可能意味着它们不应该派生

如果不让 Game 类明确了解各种 Player 类的实际实现,我无法为该问题提出一个好的解决方案。 我总是可以在 Game 类中假设永远只有人类和计算机玩家并有效地关闭它以进行扩展,但这似乎不是好的 OO 编程。

对此的任何意见将不胜感激。

【问题讨论】:

标签: language-agnostic oop ooad open-closed-principle


【解决方案1】:

我认为你不应该让 Game 类处理 IO。 这样,(阻塞的)TakeTurn 方法将从游戏板中隐藏执行方式。它可以使用其他对象与用户进行通信。

Game 类应该关注的只是棋盘和转牌的状态。玩家应该都实现一个单一的 Player 接口,并在游戏中隐藏所有实现。

【讨论】:

  • +1 表示努力赋予游戏良好的凝聚力(适当的责任)。
【解决方案2】:

我可能不会有两个HumanPlayerComputerPlayer 类,而是一个Player 类,它在创建时使用正确的输入策略进行配置。

玩家获取信息以决定其在下一回合中的移动的方式是唯一不同的东西(至少与最初的问题描述不同),所以只需将其封装在一个单独的抽象。

任何设置游戏的高级类也应该创建两组玩家(一组是人类,另一组是计算机模拟的),为每组玩家设置适当的输入策略,然后简单地将这些玩家对象提供给游戏对象.然后,Game 类将只在给定的玩家列表中调用TakeTurn 方法,对于每个新回合。

【讨论】:

    【解决方案3】:

    我不确定这是不是你想要的

    public abstract class Player 
    {
      int position;
      DecisionMaker decisionDependency;
    
      ...
    
      public void TakeTurn()
      {
        position += RollDice();
        GameOption option GetOptions(position);
        MakeDescion(option);
      }
    
      protected int RollDice()
      {
        //do something to get the movement
      }
    
      protected abstract void MakeDecision(GameOption option);
    
    }
    
    Public class ComputerPlayer : Player
    {
      public ComputerPlayer()
      {
        decisionDependency = new AIDecisionMaker();
      }
    
      protected override void void MakeDecision(GameOption option)
      {
        decisionDependency.MakeDecision(option);
        //do stuff, probably delgate toan AI based dependency
      }
    }
    
    Public class HumanPlayer : Player
    {
      public HumanPlayer()
      {
        decisionDependency = new UIDecisionMaker();
      }
    
      protected override void void MakeDecision(GameOption option)
      {
        decisionDependency.MakeDecision(option);
        //do stuff, probably interacting with the a UI or delgate to a dependency
      }
    }
    

    【讨论】:

      【解决方案4】:

      而不是 Game 类对所有玩家调用 TakeTurn,玩家应该调用 TakeTurn Game 类和 Game 类上的 应该验证是否轮到了正确的玩家。

      这应该有助于解决用户计算机播放器问题。

      【讨论】:

        【解决方案5】:

        如果游戏正在管理游戏状态并且进行 I/O,则游戏做的太多了。

        您希望 Game 只关注规则、回合和状态变化。 游戏不知道玩家是什么;它只知道它有玩家。

        您希望玩家检查游戏状态并在轮到他们时执行法律行动。

        人类玩家和整个游戏共享一个通用 I/O 包,该包显示游戏状态并提示人类输入。

        您可以通过将 I/O 包设置为游戏的 Observer 来充分利用 Java Observable。这样一来,游戏状态更改就会报告给 I/O 以供显示或记录或两者兼而有之。

        【讨论】:

          【解决方案6】:

          我会说,Game 类不应该关心这是计算机玩家还是人类玩家。它应该总是在下一个播放器类上调用 TakeTurn。如果这是一个人类玩家,则 Player 类负责与用户交流并询问用户该做什么。这意味着它将阻止直到用户下定决心。由于 UI 交互通常发生在应用程序的主线程中,因此唯一重要的是阻塞 TakeTurn 不会阻塞整个应用程序,否则在 Game 时无法处理用户输入 等待 TakeTurn

          【讨论】:

            【解决方案7】:

            Player 呈现给Game 的界面与派生的Player 类的行为正交。

            TakeTurn 的实现因 Player 对象的具体类型而异,这一事实不应引起关注。

            【讨论】:

              【解决方案8】:

              我认为Game 类不应该关心 Player 类的任何实现,也应该忽略用户界面。

              任何用户输入都需要由HumanPlayer 类处理。

              【讨论】:

                【解决方案9】:

                与其告诉游戏类只有一个人,为什么不让它在游戏的菜单/初始化期间获得该输入?如果有更多玩家,可以在游戏类初始化之前通过某种形式的输入(在菜单中选择玩家)来决定。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-11-17
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-09-15
                  • 2011-01-16
                  相关资源
                  最近更新 更多