Flutter從頭到尾設計一款簡單的五子棋遊戲(三) | 具體代碼設計

語言: CN / TW / HK

image.png 開啟掘金成長之旅!這是我參與「掘金日新計劃 · 12 月更文挑戰」的第12天,點擊查看活動詳情

前言

在前面兩篇文章中,我們已經介紹了設計一款五子棋遊戲的所使用的一些思想以及部分的代碼,以便我們更好地對設計模式進行理解。這次我們全面把代碼鋪開來講,注意,我們這次的設計是完全使用Flutter自帶的包進行設計。

正文

1.前情回顧

上一篇文章中,我們對棋子的代碼進行了設計。現在我們再進行回顧一下。先放個整個項目所使用到的類圖的概覽圖。

image.png

棋子創建——享元模式

對於棋子的創建,我們使用享元模式,因為享元模式的設計思路就是將大量相同的對象進行復用。

在本項目中,棋子創建的類圖如下:

image.png

在編碼上,我們定義了抽象類Chess,他本質上是一個抽象享元。

```Dart ///棋子的抽象類
///使用了橋接模式,外觀和顏色是兩個不同的維度
abstract class Chess{

Color? _color;

Color get color => _color!;

ChessShape? _chessShape;

ChessShape get chessShape => _chessShape!;

set chessShape(ChessShape? __chessShape);
} ```

隨後對具體的兩個棋子進行了設計。也就是設計模式中的具體享元進行設計。

而對於具體的黑白棋子,則需要繼承自Chess類

```Dart class BlackChess extends Chess{
  BlackChess() {
    _color = Colors.black;
  }

set chessShape(ChessShape? __chessShape) {
    super._chessShape = __chessShape;
  }
} ```

白色棋子同理,代碼如下:

```Dart class WhiteChess extends Chess{
  WhiteChess() {
    _color = Colors.white;
  }

set chessShape(ChessShape? __chessShape) {
    super._chessShape = __chessShape;
  }
} ```

而對於享元工廠,我們則可以使用單例模式進行創建。

類圖如下:

image.png

代碼如下:

```Dart class ChessFlyweightFactory {
  ChessFlyweightFactory._();

static ChessFlyweightFactory? _factory;

static ChessFlyweightFactory getInstance() {
    if ( _factory == null) {
_factory = ChessFlyweightFactory._();
    }
    return _factory!;
  }

HashMap _hashMap = HashMap();

Chess getChess(String type) {
    Chess chess;
    if (_hashMap[type] != null) {
      chess = _hashMap[type]!;
    } else {
      if (type == "white") {
        chess = WhiteChess();
      } else {
        chess = BlackChess();
      }
      _hashMap[type] = chess;
    }
    return chess;
  }
} ```

至此,我們對基本的棋子編寫也已經完成了。

2.其他設計

棋子形狀——橋接模式

因為形狀主要是用來裝飾棋子的,比如玩家根據自身等級可以購買不同的棋子皮膚、形狀,因此我們這裏抽出來單獨設計。這裏使用到

在本次的設計中,我們的棋子可以有方棋子和圓棋子。

類圖如下:

image.png

由於ChessWhiteChessBlackChess已經在上方給出了代碼, 因此這裏我們只給出剩下的三個類。

```Dart abstract class ChessShape{
  int? _shape;

int get shape => _shape!;

set shape(int value) {
    _shape = value;
  }
} ```

ChessShape是一個抽象類,定義了其返回的形狀類型。這裏我們使用1代表圓,0代表方。具體的類設計如下:

``` class CircleShape extends ChessShape{
  CircleShape(){
    shape = 1;
  }
}

class RectShape extends ChessShape{
  RectShape(){
    shape = 2;
  }
} ```

玩家狀態的切換——狀態模式

遊戲玩家狀態的可以進行切換,如是否可以悔棋等,因此在這裏使用了狀態模式。

image.png

玩家類設計如下:

```Dart class UserContext {

late State _state;

State get state => _state;

UserContext(){
    _state = StartState(this);
  }

play() {
    _state.play();
  }

//悔棋只能悔棋三次
bool regretChess() {
    return _state.regretChess();
  }

// 認輸 10步之內不能認輸
bool surrender() {
    return _state.surrender();
  }

setState(State state){
    _state = state;
  }

void reset() {
    _state = StartState(this);
  }
} ```

這裏對玩家的一些動作進行了限制,比如悔棋只能悔棋三次前10步不能認輸。這個玩家類在狀態模式中也叫環境類

隨後我們設計抽象狀態類:

```Dart abstract class State {
  int _step = 0;

int get step => _step;
  int _reg = 0;

int get reg => _reg;
  UserContext _userContext;

State(UserContext userContext):_userContext = userContext;

// 悔棋只能悔棋三次
bool regretChess();

// 認輸10步之內不能認輸
bool surrender();

play() {
    _step++;
  }

} ```

隨後設計具體的狀態類:

```Dart class StartState extends State {

StartState(UserContext userContext) : super(userContext);

//悔棋只能悔棋三次
@override
  bool regretChess(){
    return false;
  }

@override
  bool surrender() {
    return false;
  }

@override
  play() {
    super.play();
    if(_step >= 4) {
      _userContext.setState(MidState(_userContext).._step = _step.._reg = _reg);
    }
  }

}
```

在開始狀態中,我們設置不能悔棋,也不能投降。當步數大於4時,我們進行狀態轉移到MidState。效果圖如下:

image.png

```Dart class MidState extends State {
  MidState(UserContext userContext) : super(userContext);

@override
  int get _reg{
    return super._reg;
  }

//悔棋只能悔棋三次
@override
  bool regretChess(){
    _reg++;
    if(_reg == 3) {
      print('切換到白熱化階段');
      _userContext.setState(EndState(_userContext).._step = _step.._reg = _reg);
    }
    return true;
  }

@override
  bool surrender() {
    return true;
  }

}
```

在這個MidState中,我們允許投降,也允許悔棋。當悔棋超過3次,則進入到EndState。效果圖如下:

image.png

```Dart class EndState extends State {
  EndState(UserContext userContext) : super(userContext);

//悔棋只能悔棋三次
@override
  regretChess(){
    return false;
  }

@override
  surrender() {
    return true;
  }

} ```

EndState中,我們只允許投降。效果圖如下:

image.png

3.總結

今天在複習了原有的享元模式上,再介紹了橋接模式以及狀態模式。關於棋子的創建以及玩家的狀態我們也就已經設計完成,後續我們將把悔棋、以及App主題變換加上,基本就大功告成了。