《GOF设计模式》—工厂方法(Factory Method)—Delphi源码示例:基于工厂方法的迷宫

来源:互联网 发布:单片机 乘法 编辑:程序博客网 时间:2024/04/29 03:52

示例:基于工厂方法的迷宫

实现:.

函数CreateMaze建造并返回一个迷宫。这个函数存在的一个问题是它对迷宫、房间、门和墙壁的类进行了硬编码。我们将引入工厂方法以使子类可以选择这些构件。

首先,我们将在MazeGame中定义工厂方法以创建迷宫、房间、墙壁和门对象;每一个工厂方法返回一个给定类型的迷宫构件。MazeGame提供一些缺省的实现,它们返回最简单的迷宫、房间、墙壁和门。

不同的游戏可以创建MazeGame的子类以特别指明一些迷宫的部件。MazeGame子类可以重定义一些或所有的工厂方法以指定产品中的变化。例如,一个BombedMazeGame可以重定义产品RoomWall以返回爆炸后的变体。

代码:

 

unit uMaze;

 

interface

 

uses

    Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls;

 

type

    {房间的四个方向}

    TDirection = (North = 0,South = 1,East = 2,West = 3);

 

const

    DirectionNames: array[TDirection] of string = ('', '', '', '西');

 

type

    {咒语}

    TSpell = class

    private

        FKey: string;

    public

        property Key: string read FKey write FKey;

    end;

 

    {迷宫构件}

    TMapSite = class

    private

        FStateMsg: string;

    public

        function Enter: Boolean; virtual; abstract;

        //---

        property StateMsg: string read FStateMsg write FStateMsg;

    end;

    {房间}

    TRoom = class(TMapSite)

    private

        FSides: array[TDirection] of TMapSite;

        FRoomNumber: Integer;

    protected

        function GetSides(Direction: TDirection): TMapSite;

        procedure SetSides(Direction: TDirection; const Value: TMapSite);

    public

        constructor Create(ARoomNumber: integer);

        destructor Destroy; override;

        //---

        function Enter: Boolean; override;

        //---

        property RoomNumber: Integer read FRoomNumber;

        property Sides[Direction: TDirection]: TMapSite read GetSides write SetSides;

    end;

    TRoomWithABomb = class(TRoom)

    private

        FBomb: boolean;

    public

        constructor Create(ARoomNumber: integer; Bombed: boolean = false);

        //---

        procedure Initialize1(Bombed: boolean);

        function HasBomb(): Boolean;

        function Enter: Boolean; override;

    end;

    TEnchantedRoom = class(TRoom)

    private

        FSpell: TSpell;

    public

        constructor Create(ARoomNumber: integer; Spell: TSpell = nil);

        destructor Destroy; override;

        //---

        function Enter: Boolean; override;

        //---

        function HasSpell(): boolean;

        function PickUpSpell(): TSpell;

    end;

    {墙壁}

    TWall = class(TMapSite)

    public

        function Enter: Boolean; override;

    end;

    TBombedWall = class(TWall)

    private

        FBomb: boolean;

    public

        constructor Create(Bombed: boolean = false);

        //---

        function Enter: Boolean; override;

        procedure Initialize1(Bombed: boolean);

    end;

    {}

    TDoor = class(TMapSite)

    private

        FRoom1,FRoom2: TRoom;

        //--门是否开启

        FIsOpen: Boolean;

        procedure Initialize(room1,room2: TRoom);

    public

        constructor Create(room1,room2: TRoom); virtual;

        destructor Destroy; override;

        //---

        function Enter: Boolean; override;

        {从一个房间(传入参数)进入另一个房间(输出结果)}

        function OtherSideFrom(Room: TRoom): TRoom;

    end;

    TDoorNeedingSpell = class(TDoor)

    private

        FSpell: TSpell;

        function TrySpell(Spell: TSpell): boolean;

    public

        constructor Create(room1,room2: TRoom); override;

        destructor Destroy; override;

        //---

        function Enter: Boolean; override;

    end;

    TRoomList = class

    private

        FItemList: TList;

        function GetCount: Integer;

        function GetItems(Index: integer): TRoom;

    protected

        procedure Clear;

    public

        constructor Create;

        destructor Destroy; override;

        //---

        function Add(const Room: TRoom): integer;

        //---

        property Count: Integer read GetCount;

        property Items[Index: integer]: TRoom read GetItems;

    end;

    {迷宫}

    TMaze = class

    private

        FRooms: TRoomList;

    public

        constructor Create;

        destructor Destroy; override;

        //---

        {在迷宫中加入一个房间}

        procedure AddRoom(Room: TRoom);

        {根据房间编号取得房间}

        function RoomNo(RoomNumber: Integer): TRoom;

    end;

    {迷宫游戏}

    TMazeGame = class

    protected

        function MakeMaze: TMaze; virtual;

        function MakeRoom(ARoomNumber: integer): TRoom; virtual;

        function MakeWall: TWall; virtual;

        function MakeDoor(r1,r2: TRoom): TDoor; virtual;

    public

        function CreateMaze: TMaze;

    end;

    {炸弹迷宫游戏}

    TBombedMazeGame = class(TMazeGame)

    protected

        function MakeRoom(ARoomNumber: integer): TRoom; override;

        function MakeWall(): TWall; override;

    end;

    {魔法迷宫游戏}

    TEnchantedMazeGame = class(TMazeGame)

    private

        function CastSpell(): TSpell;

    protected

        function MakeRoom(ARoomNumber: integer): TRoom; override;

        function MakeDoor(r1,r2: TRoom): TDoor; override;

    end;

 

var

    CurSpell: TSpell;

 

implementation

 

constructor TRoom.Create(ARoomNumber: integer);

    //---

    procedure _InitSides;

    var

        Direction: TDirection;

    begin

        for Direction := Low(FSides) to High(FSides) do

            FSides[Direction] := nil;

    end;

begin

    inherited Create;

    //---

    FRoomNumber := ARoomNumber;

    //---

    _InitSides;

end;

 

destructor TRoom.Destroy;

    //---

    procedure _ClearSides;

    var

        Direction: TDirection;

    begin

        for Direction := Low(FSides) to High(FSides) do

        begin

            if FSides[Direction] <> nil then

                FSides[Direction].Free;

        end;

    end;

begin

    _ClearSides;

    //---

    inherited;

end;

 

function TRoom.Enter: Boolean;

begin

    self.StateMsg := format('进入房间%d', [FRoomNumber]);

    Result := true;

end;

 

function TRoom.GetSides(Direction: TDirection): TMapSite;

begin

    Result := FSides[Direction];

end;

 

procedure TRoom.SetSides(Direction: TDirection; const Value: TMapSite);

begin

    FSides[Direction] := Value;

end;

 

function TWall.Enter: Boolean;

begin

    self.StateMsg := '碰到墙';

    Result := false;

end;

 

constructor TDoor.Create;

begin

    inherited Create;

    //---

    Initialize(room1,room2);

end;

 

destructor TDoor.Destroy;

    //---

    procedure _ClearDoor(Room: TRoom);

    var

        Direction: TDirection;

    begin

        if Room <> nil then

        begin

            with Room do

            begin

                for Direction := Low(TDirection) to High(TDirection) do

                begin

                    if Sides[Direction] = self then

                    begin

                        Sides[Direction] := nil;

                        exit;

                    end;

                end;

            end;

        end;

    end;

begin

    _ClearDoor(FRoom1);

    _ClearDoor(FRoom2);

    //---

    inherited;

end;

 

function TDoor.Enter: Boolean;

begin

    self.StateMsg := '碰到门';

    Result := true;

end;

 

procedure TDoor.Initialize(room1,room2: TRoom);

begin

    FRoom1 := room1;

    FRoom2 := room2;

    FIsOpen := False;

end;

 

function TDoor.OtherSideFrom(Room: TRoom): Troom;

begin

    if Room = FRoom1 then

        Result := FRoom2

    else

        Result := FRoom1;

end;

 

constructor TBombedWall.Create(Bombed: boolean);

begin

    inherited Create;

    //---

    Initialize1(Bombed);

end;

 

function TBombedWall.Enter: Boolean;

begin

    if FBomb then

    begin

        self.StateMsg := '碰到炸弹墙';

        Result := false;

    end

    else

        Result := inherited Enter;

end;

 

procedure TBombedWall.Intialize(Bombed: boolean);

begin

    FBomb := Bombed;

end;

 

constructor TDoorNeedingSpell.Create(room1,room2: TRoom);

begin

    inherited;

    //---

    FSpell := TSpell.Create;

    FSpell.Key := '123';

end;

 

destructor TDoorNeedingSpell.Destroy;

begin

    FSpell.Free;

    //---

    inherited;

end;

 

function TDoorNeedingSpell.Enter: Boolean;

begin

    Result := TrySpell(CurSpell);

    if Result then

        self.StateMsg := '碰到门,使用了正确的咒语卷轴'

    else

        self.StateMsg := '碰到门,使用了错误的咒语卷轴';

end;

 

function TDoorNeedingSpell.TrySpell(Spell: TSpell): boolean;

begin

    Result := FSpell.Key = Spell.Key;

end;

 

constructor TRoomWithABomb.Create(ARoomNumber: integer; Bombed: boolean);

begin

    inherited Create(ARoomNumber);

    //---

    Initialize1(Bombed);

end;

 

function TRoomWithABomb.Enter: Boolean;

begin

    if HasBomb then

    begin

        self.StateMsg := format('进入有炸弹的房间%d', [FRoomNumber]);

        Result := true;

    end

    else

        Result := inherited Enter;

end;

 

function TRoomWithABomb.HasBomb: Boolean;

begin

    Result := FBomb;

end;

 

procedure TRoomWithABomb.Intialize(Bombed: boolean);

begin

    FBomb := Bombed;

end;

 

constructor TEnchantedRoom.Create(ARoomNumber: integer; Spell: TSpell);

begin

    inherited Create(ARoomNumber);

    //---

    FSpell := Spell;

end;

 

destructor TEnchantedRoom.Destroy;

begin

    if FSpell <> nil then

        FSpell.Free;

    //---

    inherited;

end;

 

function TEnchantedRoom.Enter: Boolean;

begin

    if HasSpell then

    begin

        CurSpell := PickUpSpell;

        self.StateMsg := format('进入房间%d,拿起咒语卷轴', [FRoomNumber]);

        Result := true;

    end

    else

        Result := inherited Enter;

end;

 

function TEnchantedRoom.HasSpell: boolean;

begin

    Result := FSpell <> nil;

end;

 

function TEnchantedRoom.PickUpSpell: TSpell;

begin

    Result := FSpell;

end;

 

constructor TMaze.Create;

begin

    inherited;

    //---

    FRooms := TRoomList.Create;

end;

 

destructor TMaze.Destroy;

begin

    FRooms.Free;

    //---

    inherited;

end;

 

procedure TMaze.AddRoom(Room: TRoom);

begin

    FRooms.Add(Room);

end;

 

function TMaze.RoomNo(RoomNumber: Integer): TRoom;

var

    i: Integer;

begin

    Result := nil;

    //---

    with FRooms do

    begin

        for i := 0 to Count - 1 do

        begin

            if Items[i].Roomnumber = RoomNumber then

            begin

                Result := Items[i];

                Exit;

            end;

        end;

    end;

end;

 

function TMazeGame.CreateMaze: TMaze;

var

    aMaze: TMaze;

    r1,r2: Troom;

    theDoor: TDoor;

begin

    //---建构一个maze,有两个Room,一个Door,六面Wall

    aMaze := MakeMaze;

    //---

    r1 := MakeRoom(1);

    r2 := MakeRoom(2);

    //---

    theDoor := MakeDoor(r1,r2);

    //---

    aMaze.AddRoom(r1);

    aMaze.AddRoom(r2);

    //---

    r1.SetSides(North,MakeWall());

    r1.SetSides(East,theDoor);

    r1.SetSides(South,MakeWall());

    r1.SetSides(West,MakeWall());

    //---

    r2.SetSides(North,MakeWall());

    r2.SetSides(East,MakeWall());

    r2.SetSides(South,MakeWall());

    r2.SetSides(West,theDoor);

    //---

    result := aMaze;

end;

 

function TMazeGame.MakeMaze: TMaze;

begin

    Result := TMaze.Create;

end;

 

function TMazeGame.MakeRoom(ARoomNumber: integer): TRoom;

begin

    Result := TRoom.Create(ARoomNumber);

end;

 

function TMazeGame.MakeWall: TWall;

begin

    Result := TWall.Create;

end;

 

function TMazeGame.MakeDoor(r1,r2: TRoom): TDoor;

begin

    Result := TDoor.Create(r1,r2);

end;

 

function TBombedMazeGame.MakeWall(): TWall;

begin

    Result := TBombedWall.Create;

end;

 

function TBombedMazeGame.MakeRoom(ARoomNumber: integer): TRoom;

begin

    Result := TRoomWithABomb.Create(ARoomNumber);

end;

 

function TEnchantedMazeGame.MakeRoom(ARoomNumber: integer): TRoom;

begin

    Result := TEnchantedRoom.Create(ARoomNumber,CastSpell);

end;

 

function TEnchantedMazeGame.CastSpell(): TSpell;

begin

    Result := TSpell.Create;

    Result.Key := '123';

end;

 

function TEnchantedMazeGame.MakeDoor(r1,r2: TRoom): TDoor;

begin

    Result := TDoorNeedingSpell.Create(r1,r2);

end;

 

constructor TRoomList.Create;

begin

    inherited;

    //---

    FItemList := TList.Create;

end;

 

destructor TRoomList.Destroy;

begin

    Clear;

    FItemList.Free;

    //---

    inherited;

end;

 

function TRoomList.Add(const Room: TRoom): integer;

begin

    if Assigned(Room) then

        Result := FItemList.Add(Room)

    else

        Result := -1;

end;

 

procedure TRoomList.Clear;

var

    i: Integer;

begin

    with FItemList do

    begin

        for i := 0 to Count - 1 do

            TObject(Items[i]).Free;

        //---

        Clear;

    end;

end;

 

function TRoomList.GetCount: Integer;

begin

    Result := FItemList.Count;

end;

 

function TRoomList.GetItems(Index: integer): TRoom;

begin

    Result := FItemList[Index];

end;

 

end.

 

unit Unit1;

 

interface

 

uses

    Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,

    Dialogs,StdCtrls,Menus,uMaze;

 

type

    TForm1 = class(TForm)

        ListBox1: TListBox;

        procedure FormCreate(Sender: TObject);

        procedure FormDestroy(Sender: TObject);

        procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);

        procedure ListBox1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);

    private

        FMazeGame: TMazeGame;

        FMaze: TMaze;

        FCurRoom: TRoom;

    public

    { Public declarations }

    end;

 

var

    Form1: TForm1;

 

implementation

 

{$R *.dfm}

 

procedure TForm1.FormCreate(Sender: TObject);

begin

    self.KeyPreview := true;

    //---

    {FMazeGame := TMazeGame.Create;

    FMaze := FMazeGame.CreateMaze;}

    //---

    {FMazeGame := TBombedMazeGame.Create;

    FMaze := FMazeGame.CreateMaze;

    TRoomWithABomb(FMaze.RoomNo(2)).Initialize1(true);}

    //---

    FMazeGame := TEnchantedMazeGame.Create;

    FMaze := FMazeGame.CreateMaze;

    //---

    FCurRoom := FMaze.RoomNo(1);

    with FCurRoom do

    begin

        Enter;

        ListBox1.Items.Add(StateMsg);

    end;

end;

 

procedure TForm1.FormDestroy(Sender: TObject);

begin

    FMaze.Free;

    FMazeGame.Free;

end;

 

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift:

    TShiftState);

    //---

    procedure _EnterRoomSide(Direction: TDirection);

    var

        ARoom: TRoom;

    begin

        with FCurRoom do

        begin

            if Sides[Direction] <> nil then

            begin

                with Sides[Direction] do

                begin

                    if Enter then

                    begin

                        ListBox1.Items.Add(DirectionNames[Direction] + ':' + StateMsg);

                        //---

                        if Sides[Direction] is TDoor then

                        begin

                            ARoom := TDoor(Sides[Direction]).OtherSideFrom(FCurRoom);

                            if ARoom <> nil then

                            begin

                                if ARoom.Enter then

                                    FCurRoom := ARoom;

                                ListBox1.Items.Add(ARoom.StateMsg);

                            end;

                        end;

                    end

                    else

                        ListBox1.Items.Add(DirectionNames[Direction] + ':' + StateMsg);

                end;

            end;

        end;

    end;

begin

    case Ord(Key) of

        VK_LEFT: _EnterRoomSide(East);

        VK_RIGHT: _EnterRoomSide(West);

        VK_UP: _EnterRoomSide(South);

        VK_DOWN: _EnterRoomSide(North);

    end;

end;

 

procedure TForm1.ListBox1KeyDown(Sender: TObject; var Key: Word; Shift:

    TShiftState);

begin

    Key := 0;

end;

 

end.

 

 

原创粉丝点击