Python的創建型設計模式之工廠設計模式

工廠,就是創造生產東西的地方。因此,工廠設計模式就是直接創造你所需要的類以供使用的時候直接拿來用即可。

對比前兩種設計模式,工廠設計模式,筆者認爲能更好的對你所做的東西進行規劃處理,條理能更清楚一點。這也是爲什麼此書把工廠設計模式作爲重點的原因吧。

書上的例子是用工廠設計模式來創建兩個棋盤——國際象棋和國際跳棋。


1.生產的是棋盤

2.分類爲象棋和跳棋

3.對應的棋子創建

以上是最基本的工廠設計模式。來看看關鍵代碼

BLACK, WHITE = ("BLACK", "WHITE")


class AbstractBoard:

    def __init__(self, rows, columns):
        self.board = [[None for _ in range(columns)] for _ in range(rows)]
        self.populate_board()

    def populate_board(self):
        raise NotImplementedError()

    def __str__(self):
        squares = []
        for y, row in enumerate(self.board):
            for x, piece in enumerate(row):
                square = (piece, BLACK if (y + x) % 2 else WHITE)
                squares.append(square)
            squares.append('\n')
        return "".join(squares)


class CheckersBoard(AbstractBoard):

    def __init__(self):
        super(CheckersBoard, self).__init__(10, 10)

    def populate_board(self):
        for x in range(0, 9, 2):
            for row in range(4):
                column = x + ((row + 1) % 2)
                self.board[row][column] = BlackDraught()
                self.board[row+6][column] = WhiteDraught()


class Piece(str):
    __slots__ = ()


class BlackDraught(Piece):
    __slots__ = ()

    def __new__(Class):
        return super.__new__(Class, "\N{black draughts man}")


class WhiteDraught():
    pass


class ChessBoard(AbstractBoard):

    def __init__(self):
        super(ChessBoard, self).__init__(10, 10)

    def populate_board(self):
        self.board[0][0] = BlackChessRook()
        self.board[9][9] = WhiteChessRook()
        for column in range(10):
            self.board[1][column] = BlackChessPawn()
            self.board[6][column] = WhiteChessPawn()


class BlackChessRook():
    pass


class WhiteChessRook():
    pass


class BlackChessPawn():
    pass


class WhiteChessPawn():
    pass


def main():
    checkers = CheckersBoard()
    print checkers

    chess = ChessBoard()
    print chess
如果你現在看懂了以上代碼的邏輯,那麼恭喜你,工廠設計模式你已經掌握了。但不要忘了我們用的 Python ,這種設計模式可以繼續修改的。

上面的代碼中,尤其看下象棋的棋子設計,這裏僅僅是列出了幾個棋子的類,回想之前的設計模式,是不是可以對棋子類進行優化,在棋盤創建實例化棋子時更方便更簡單呢。這裏解決的辦法是使用 eval 和 exec 來解決,專業術語爲動態執行字符串代碼(也可以是文件)

class CheckersBoard(AbstractBoard):

    def __init__(self):
        super(CheckersBoard, self).__init__(10, 10)

    def populate_board(self):
        for x in range(0, 9, 2):
            for y in range(4):
                column = x +((y + 1) % 2)
                for row, color in ((y, 'black'), (y + 6, 'white')):
                    self.board[row][column] = create_piece("draught", color)


def create_piece(kind, color):
    if kind == "draught":
        return eval("{}{}()".format(color.title(), kind.title()))
    return eval("{}{}()".format(color.title(), kind.title()))

for code in itertools.chain((0x26C0, 0x26C2), range(0x2654, 0x2660)):
    char = chr(code)
    name = unicodedata.name(char).title().replace(" ", "")
    if name.endwith("sMan"):
        name = name[:-4]
    exec ("""
    class{}(piece):
        __slots__ = ()
        def __new__(Class):
            return super.__new__(Class, "{}")""").format(name, char)
國際象棋的棋子,用 for 循環來獲得對應的棋子樣式,然後用 exec 動態創建對應棋子的類,不用手動一個個的自己創建。

國際跳棋的棋子,用 if 來判斷 kind 之後,使用 eval 來返回對應的棋子類。

這樣比之前的過程能簡化了很多。但是,動態創建說是有風險的。關於 excl 和 eval 的基礎學習可參考:

python 的 exec、eval http://www.mojidong.com/python/2013/05/10/python-exec-eval/

因此,強大的 Python 繼續進行修改,這次直接對抽象類的棋盤增加棋盤和棋子類的初始化。

一是在靜態常量裏面定義棋子名稱,二是在抽象棋盤裏面可確定棋子類

BLACK, WHITE = ("BLACK", "WHITE")
DRAUGHT, PAWN, ROOK, KNIGHT, BISHOP, KING, QUEEN = ("DRAUGHT", "PAWN", "ROOK", "KNIGHT", "BISHOP", "KING", "QUEEN")


class AbstractBoard:
    __classForPiece = {(DRAUGHT, BLACK): BlackDraught,
                       (PAWN, BLACK): BlackChessPawn,
                       (QUEEN, WHITE): WhiteChessQueen}

    def __init__(self, rows, columns):
        self.board = [[None for _ in range(columns)] for _ in range(rows)]
        self.populate_board()

    def populate_board(self):
        raise NotImplementedError()

    def __str__(self):
        squares = []
        for y, row in enumerate(self.board):
            for x, piece in enumerate(row):
                square = (piece, BLACK if (y + x) % 2 else WHITE)
                squares.append(square)
            squares.append('\n')
        return "".join(squares)

    def create_piece(self, kind, color):
        return AbstractBoard.__classForPiece[kind, color]()


class CheckersBoard(AbstractBoard):

    def __int__(self):
        super(CheckersBoard, self).__init__(10, 10)

    def populate_board(self):
        for x in range(0, 9, 2):
            for y in range(4):
                column = x + ((y + 1) % 2)
            for row, color in ((y, BLACK), (y + 6, WHITE)):
                self.board[row][column] = self.create_piece(DRAUGHT, color)
classForPiece 是定義的一個元組與棋子類名稱的字典, create 方法就可以根據 kind 和 color 來得到對應棋子的類。例如這個跳棋中,直接傳參創建即可。

象棋的話,這裏在創建的話,也就可以是一句 self.board = [row][column] = self.create_piece(QUEEN, color) ,不用管顏色問題了。


以上,從工廠設計模式來進行兩個棋盤的創建,最後進行了優化和改進。不僅瞭解工廠設計模式,還學習了 Python 些許功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章