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 些许功能。

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