OOP 设计以两种方式避免对象另一个对象

Posted

技术标签:

【中文标题】OOP 设计以两种方式避免对象另一个对象【英文标题】:OOP Design to Avoid Object Another Object in Two Ways 【发布时间】:2015-07-26 22:36:39 【问题描述】:

我正在编写一个 Java 国际象棋游戏并试图整理设计。以下是我目前的课程:

游戏:由 2 个玩家对象和一个棋盘对象组成

玩家:

Board:由 Square 对象的 2d 数组 (8 x 8) 组成,每个对象上可能有/可能没有一块

国王、王后、车、主教、骑士、兵:

片段:上述片段的超类,共享功能在这里

这是我不喜欢的:我的 Square 类中有一个名为“board”的字段,这样我就可以引用给定正方形所属的 board。我不喜欢这种设计,因为它会导致克隆问题。

所以我想在不做这种回溯的情况下重写片断类中的一些功能。板子能够访问它的每个方块是很棒的,但我不想让 Square 类有一个 getBoard() 函数。

这是一个我需要重写的函数示例:

public Square getSquareOneMoveAway(Square start, int heightChange, int widthChange) 

    Square candidateSquare = start.getBoard().getNearbySquare(start, heightChange, widthChange);

    if (candidateSquare != null) 
        if (candidateSquare.isEmpty()) 
            return candidateSquare;
         else if (! candidateSquare.getPiece().getColor().equals(start.getPiece().getColor())) 
            return candidateSquare;
        
        else 
            return null;
        
    
    else 
        return null;
    

请注意,在我的 Player 类中,我有如下方法:

canMovePiece(Square start, Square end, Board board), movePiece(Square start, Square end, Board board)等

我是否应该将此方法移动到 Board 类中,这样我就可以访问 board 对象而不必从 Square 转到 board?

非常感谢任何想法, 布莱曼

【问题讨论】:

这可能更适合 Code Review,尽管 Code Review 的人会因为我这样说而对我大喊大叫。 “板”就是这样,一块板。它不知道上面播放的是什么。 chess 棋盘可能...改变这些能力的国际象棋变体。有很多个选项。 @DaveNewton 我们只在您出于这个原因投票关闭时才会大喊大叫,以及向 CR 推荐公然 CR 偏离主题的帖子。感谢您仍然有勇气推荐CR! 我认为您可以对您的代码进行全面审查。包含所有相关类的代码,让Code Review的人施展魔法吧! (假设您已实现您的设计并且它按预期工作 【参考方案1】:

在我看来,您过度设计了问题。用另一个类来代表棋盘的方格真的有什么好处吗?如果不是(而且我不这么认为),我将摆脱 Square 类。

class Board 
    public static final int MAX_ROWS = 8;
    public static final int MAX_COLS = 8;

    private Piece[][] squares;

    public Board() 
        squares = new Piece[ MAX_ROWS ][ MAX_COLS ];
    

    // ...

不过,我有一个 Position 类,包含一对行和列。

希望这会有所帮助。

【讨论】:

那么你会不会让一个棋盘是一组棋子,其中许多是空的(代表空的)? 是的。板子是固定的,不是很大,为什么不呢?除非您在嵌入式系统中进行编程,否则内存会受到严重限制,否则您可以负担得起 (8x8=)64 个位置的数组。没什么大不了的。【参考方案2】:

ChessBoard 的以下定义用于存储棋子、存储它们的位置,并使用该信息来确定给定的移动是否有效。

class ChessBoard implements Board 
    Piece[][] pieces; // null if vacant at that position

    @Override
    public boolean isValidMove(Player player, Square start, Square end) 
        Piece pieceStart = pieces[start.row][start.col];
        Piece pieceEnd = pieces[end.row][end.col];

        return (
            pieceStart != null &&
            pieceStart.getColor() == player.getColor() &&
            pieceStart.isValidTransition(start, end) &&
            (pieceEnd == null || pieceStart.canDefeat(pieceEnd))
        );
    

请注意,移动是否有效的逻辑被封装在被移动的片段中。

class Pawn implements Piece 

    @Override
    public boolean isValidTransition(Square start, Square end) 
        int dH = end.col - start.col;
        int dW = end.row - start.row;
        // return if is valid transition for a pawn in terms of dH and dW
    

    @Override
    public boolean canDefeat(Piece otherPiece)  
        return (
            this.getColor() != otherPiece.getColor() &&
            this.getStrength() > otherPiece.getStrength()
        );
    

    @Override
    public int getStrength()  ... 

    @Override
    public Color getColor()  ... 

square 类只是一个简单的包含行和列的 2 元组。

class Square 
    int row, col;

【讨论】:

【参考方案3】:

我是一个实用主义者,所以我不会将片段作为类,而只是枚举。所以:

enum Pieces 

   Pawn,
   Rook,
   Knight
   // etc
;

然后我会像这样创建一个名为“Rule”的接口:

interface Rule

   void addRule(Pieces p);
;

其中,通过让一个类实现规则,我可以决定单位在游戏中如何交互(或者最好是在游戏中交互)。此类将是 PawnRule、QueenRule 等。这样做,可以向游戏中添加尽可能多的规则并加以利用。这不仅有利于可扩展性,而且为游戏本身带来了可行的基础。

class PawnRule implements Rule

    private boolean pFirstMove = true;
    // etc
    void addRule(Pieces p)
    
        if (Square.hasSpace(Space.FRONT) * 2 && pFirstMove)
        
            // where renderer holds 2D/3D data pertaining to the piece
            rendrerer.getModel("Pawn").moveByTwo(); // somehow
        
    

在设计方面,需要考虑的因素很多;并且可能不是您要查找的枚举。我们也不知道游戏是基于文本的、2D 的还是 3D 的。一般来说,如果它是基于文本的,则更容易实现这些东西,但如果它是 3D 则更复杂。我建议您绘制一个类图,甚至是一个通信图,以便您了解国际象棋游戏中的类/枚举/接口如何相互交互。

【讨论】:

以上是关于OOP 设计以两种方式避免对象另一个对象的主要内容,如果未能解决你的问题,请参考以下文章

多对多字段django以两种方式添加关系

Knockout.js - 如何使用“foreach”以两种不同的方式显示项目列表?

OOP 中的 组合聚合和关联有什么区别?

OOP的三大特性是啥

OOP的三大特性是啥?

2021年最新Python讲义:面向对象(OOP)基本概念