生命游戏: - 需要帮助在生命游戏中实施SOLID原则

时间:2015-06-07 15:04:30

标签: java eclipse conways-game-of-life

您好我正在尝试通过应用原则开启和关闭,单一责任原则,liskov替换原则,接口隔离原则和依赖倒置原则来执行生命游戏程序。但是我被困住了,无法想到我应该在哪里应用原则。我是新手,如果有人知道有人可以帮助我了解如何继续或如何应用它我真的很感激。谢谢你提前。 我正在上传某些代码部分作为应该应用此原则的示例。

Abstractgame stratedgy

public abstract class AbstractGameStratedgy implements GameStratedy {

    private Rule [] rules; 

    @Override
    public void setRules(Rule[] rules) {
        this.rules = rules;
    }

    @Override
    public Rule[] getRules() {
        return rules;
    }

    @Override
    public Set<Cell> findNeighbours(Cell cell,Set<Cell> liveCells)
    {
        HashSet<Cell> neighbours=new HashSet<Cell>(); 

        int x=cell.getX();
        int y=cell.getY();
        Cell tempCell;
        for(int i=x-1;i<=x+1;i++)
        {
            if(i<0) continue;

            for(int j=y-1;j<=y+1;j++)
            {
                if(j<0) continue;

                tempCell=new Cell(i, j);
                if(liveCells.contains(tempCell))
                {
                    tempCell.setState(State.LIVE);
                }
                neighbours.add(tempCell);
            }
        }

        return neighbours;
    }
}

细胞

public class Cell {

    private final int x,y;
    private State state;

    public Cell(int x,int y)
    {
        this(x,y,State.DEAD);
    }

    public Cell(int x,int y,State state)
    {
        this.x=x;
        this.y=y;
        this.state = state;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + x;
        result = prime * result + y;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Cell other = (Cell) obj;
        if (x != other.x)
            return false;
        if (y != other.y)
            return false;
        return true;
    }

    public Cell createCopy() {
        return new Cell(x,y,state);
    }

}

RuleRunner

public class RuleRunner {

    private GameStratedy gameStratedy;

    public RuleRunner(GameStratedy gameStratedy)
    {
        this.gameStratedy = gameStratedy;
    }


    public Set<Cell> applyRules(Set<Cell> liveCells) {
        HashSet<Cell> nextGeneration=new HashSet<Cell>(); 

        Set<Cell> neighbouringCells;
        for(Cell cellFromCurrentGeneration: liveCells)
        {
            processCell(cellFromCurrentGeneration,liveCells,nextGeneration);

            neighbouringCells=gameStratedy.findNeighbours(cellFromCurrentGeneration, liveCells);

            for(Cell neighbouringCell:neighbouringCells)
            {
                processCell(neighbouringCell,liveCells,nextGeneration);
            }
        }

        return filterDead(nextGeneration);
    }

    private Set<Cell> filterDead(HashSet<Cell> nextGeneration) {
        Iterator<Cell> iterator = nextGeneration.iterator();

        while(iterator.hasNext())
        {
            if(State.DEAD.equals(iterator.next().getState()))
            {
                iterator.remove();
            }
        }

        return nextGeneration;
    }


    private void processCell(Cell cell,Set<Cell> currentGeneration,Set<Cell> nextGeneration)
    {
        if(nextGeneration.contains(cell)) return; // already processed

        cell=cell.createCopy();

        State nextState=cell.getState();
        for(Rule rule:gameStratedy.getRules())
        {
            nextState=rule.nextState(cell.getState(), findLiveNeighbourCount(cell, currentGeneration));

            if(!cell.getState().equals(nextState))
            {
                break;
            }
        }

        cell.setState(nextState);
        nextGeneration.add(cell);
    }



    private int findLiveNeighbourCount(Cell cell,Set<Cell> liveCells)
    {
        int count=0;
        for(Cell c:gameStratedy.findNeighbours(cell, liveCells))
        {
            if(State.LIVE.equals(c.getState())) count++;
        }
        return count;
    }

}

1 个答案:

答案 0 :(得分:2)

我之前只分享了我实现的Github存储库的README.md文件的链接,主持人对我表示了真正的关注,如果链接后面的页面被删除了怎么办?我提供了一个答案,我希望这有助于理解解决方案,而无需参考我的README.md文件或代码。

这个问题是从一年前开始的。您可能已经想出了SOLID原则。因为,我最近在PHP OOP中解决了Conway的生命游戏并应用了SOLID设计原则,目的是与同事在工作中分享我对原理的理解,我在这里分享我解决这个问题的方法。 stackoverflow作为这个问题的答案,到目前为止还没有答案。我希望这对所有到达这里的人都有帮助,他们希望通过在康威的生命游戏问题上练习来专注于SOLID原则。

以下是我需要在哪些课程上进​​行思考的进展:

步骤1:我开始使用问题陈述中明显的类,即 Board Cell ,该板由许多单元组成。

第2步:在应用单一责任原则时,一个班级应该只有一个责任,只有一个原因需要改变, Board Cell < / em>我将这两个类分解为这些类:

  • Board (用于维护电路板尺寸和活动单元位置以及getNeighbourCount()方法)
  • BoardRenderer (用于在介质上绘制电路板)
  • BoardPersister (用于持久化,即在步骤之间将板布局内存保存到MySQL之类的持久存储中)
  • BoardInitializer (用于游戏开始时板上活细胞的初始排列)
  • 对于我只需要 CellRenderer 的细胞,因为此时我开始了 实现我的Cell类除了要渲染之外别无其他工作要做 (单向活动单元,另一种方式是非活动单元)。这是董事会 这将了解其尺寸和位置 活跃细胞(因此也是非活动细胞的位置)。一世 不需要 Cell 类。

步骤3:接下来我对打开/关闭和依赖性倒置原则的理解是,它们分别表示,软件实体应该是开放的扩展但是关闭以进行修改,并且应该依赖于抽象和不是在结构上(来源维基百科),我将 BoardInitializer BoardRenderer BoardPersister CellRenderer 从类转换为接口,以便我可以通过简单地替换现有的类来构建新类并使用它们。这些新类只需要实现各自的接口。

在这个阶段我意识到并且我还需要一个游戏控制器。所以我添加了一个带有newGame()和advanceGame()方法的 GameController 抽象类和一个 GameRenderer 接口来主要渲染游戏控件。我还将 Board 转换为抽象类,以便能够有两种类型的板: EdgesWrappedBoard BoundedBoard GameRenderer 会调用 BoardRenderer ,然后使用 CellRenderer 绘制活动和非活动单元格。

所以,现在我们有了这些类,

抽象类:

  • GameController

扩展抽象类的类:

  • EdgesWrappedBoard (扩展 Board
  • BoundedBoard (扩展 Board
  • HTMLGameController (扩展 GameController
  • ConsoleGameController (扩展 GameController

接口:

  • GameRenderer
  • BoardRenderer
  • 的CellRenderer
  • BoardInitializer
  • BoardPersister

实现接口的类:

  • HTMLGameRenderer (实现 GameRenderer
  • ConsoleGameRenderer (实现 GameRenderer
  • HTMLBoardRenderer (实现 BoardRenderer
  • ConsoleBoardRenderer (实现 BoardRenderer
  • HTMLCellRenderer (实现 CellRenderer
  • ConsoleCellRenderer (实现 CellRenderer
  • RandomBoardInitializer (实现 BoardInitializer
  • Test1BoardInitializer (实现 BoardInitializer
  • Test2BoardInitializer (实现 BoardInitializer
  • FileBoardPersister (实现 BoardPersister
  • MySQLBoardPersister (实现 BoardPersister

步骤4:最后,我检查了是否违反了 Liskov的合法原则接口隔离原则。没有。这些原则分别是: 程序中的对象应该可以替换为其子类型的实例,而不会改变该程序的正确性。 许多客户端特定的接口比一个通用接口更好。

注意:从一开始,为了实现依赖倒置和开放/关闭原则,我开始同时拥有Web(HTML)和Console接口,并且能够持久保存到文件或数据库中。

如果您有兴趣将我的思维进展视为类图,您可以参考GitHub上共享的implementation of Game of Life的README.md文件。还有其他解决方案。我的目标是最好地将我对SOLID原则的理解传达给我在工作中的同事,这样我们就可以在OOP PHP代码中实现更好的可维护性。

我希望这对你有所帮助。请告诉我。