《GOF设计模式》—原型(Prototype)—Delphi源码示例:基于原型的迷宫1

示例:基于原型的迷宫1

实现:

我们将定义MazeFactory的子类MazePrototypeFactory。该子类将使用它要创建的对象的原型来初始化,这样我们就不需要仅仅为了改变它所创建的墙壁或房间的类而生成子类了。

用于创建墙壁、房间和门的成员函数是相似的:每个都要克隆一个原型,然后初始化。

我们只需使用基本迷宫构件的原型进行初始化,就可以由MazePrototypeFactory来创建一个基本迷宫。

为了改变迷宫的类型,我们用一个不同的原型集合(如用一个BombedDoor和一个RoomWithABomb)来初始化MazePrototypeFactory,以创建一个带炸弹的迷宫。

一个可以被用作原型的对象,例如Wall的实例,必须支持Clone操作。它还必须有一个拷贝构造器用于克隆。它可能还需要一个独立的操作来重新初始化内部状态。我们将给Door增加Initialize操作以允许客户初始化克隆对象的房间。

BombedWall子类必须重定义Clone并实现相应的拷贝构造器。虽然BombedWall.Clone返回一个TWall,但它的实现返回了一个指向子类的新实例的指针,即TBombedWall。我们在基类中这样定义Clone是为了保证克隆原型的客户不需要知道具体的子类。客户决不需要将Clone的返回值向下类型转换为所需类型。

代码:

 

 

unit uMazePrototype;

 

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

    {迷宫构件}

    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;

        procedure InitializeRoomNo(RoomNo: integer);

        function Clone: TRoom; virtual;

        //---

        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);

        //---

        function HasBomb(): Boolean;

        function Enter: Boolean; override;

        procedure Initialize(Bombed: boolean);

        function Clone: TRoom; override;

    end;

    {墙壁}

    TWall = class(TMapSite)

    public

        function Enter: Boolean; override;

        function Clone: TWall; virtual;

    end;

    TBombedWall = class(TWall)

    private

        FBomb: boolean;

    public

        constructor Create(Bombed: boolean = false);

        //---

        function Enter: Boolean; override;

        procedure Initialize(Bombed: boolean);

        function Clone: TWall; override;

    end;

    {}

    TDoor = class(TMapSite)

    private

        FRoom1,FRoom2: TRoom;

        //--门是否开启

        FIsOpen: Boolean;

    public

        constructor Create(room1,room2: TRoom);

        destructor Destroy; override;

        //---

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

        function OtherSideFrom(Room: TRoom): TRoom;

        function Enter: Boolean; override;

        procedure Initialize(room1,room2: TRoom);

        function Clone: TDoor; virtual;

    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;

        function Clone: TMaze; virtual;

    end;

    {迷宫构件工厂}

    TMazeFactory = class

    protected

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

        function MakeMaze: TMaze; virtual; abstract;

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

        function MakeWall: TWall; virtual; abstract;

    end;

    {迷宫构件原型工厂}

    TMazePrototypeFactory = class(TMazeFactory)

    private

        F_PrototypeMaze: TMaze;

        F_PrototypeWall: TWall;

        F_PrototypeRoom: TRoom;

        F_PrototypeDoor: TDoor;

    public

        constructor Create(M: TMaze; W: TWall; R: TRoom; D: TDoor); virtual;

        destructor Destroy; override;

        //---

        function MakeMaze(): TMaze; override;

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

        function MakeWall(): TWall; override;

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

    end;

    {迷宫游戏}

    TMazeGame = class

    public

        function CreateMaze(factory: TMazeFactory): TMaze;

    end;

 

implementation

 

function TRoom.Clone: TRoom;

begin

    result := TRoom.Create(self.RoomNumber);

end;

 

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.InitializeRoomNo(RoomNo: integer);

begin

    FRoomNumber := RoomNo;

end;

 

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

begin

    FSides[Direction] := Value;

end;

 

function TWall.Clone: TWall;

begin

    Result := TWall.Create;

end;

 

function TWall.Enter: Boolean;

begin

    self.StateMsg := '碰到墙';

    Result := false;

end;

 

function TDoor.Clone: TDoor;

begin

    result := TDoor.Create(nil,nil);

end;

 

constructor TDoor.Create;

begin

    inherited Create;

    //---

    self.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;

 

function TBombedWall.Clone: TWall;

var

    Wall: TBombedWall;

begin

    Wall := TBombedWall.Create;

    Wall.Initialize(self.FBomb);

    //---

    Result := Wall;

end;

 

constructor TBombedWall.Create(Bombed: boolean);

begin

    inherited Create;

    //---

    self.Initialize(Bombed);

end;

 

function TBombedWall.Enter: Boolean;

begin

    if FBomb then

    begin

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

        Result := false;

    end

    else

        Result := inherited Enter;

end;

 

procedure TBombedWall.Initialize(Bombed: boolean);

begin

    FBomb := Bombed;

end;

 

function TRoomWithABomb.Clone: TRoom;

var

    Room: TRoomWithaBomb;

begin

    Room := TRoomWithaBomb.Create(self.RoomNumber);

    Room.Initialize(self.HasBomb);

    //---

    result := Room;

end;

 

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

begin

    inherited Create(ARoomNumber);

    //---

    self.Initialize(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.Initialize(Bombed: boolean);

begin

    FBomb := Bombed;

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 TMaze.Clone: TMaze;

begin

    Result := TMaze.Create;

end;

 

function TMazeGame.CreateMaze(factory: TMazeFactory): TMaze;

var

    aMaze: TMaze;

    r1,r2: TRoom;

    theDoor: TDoor;

begin

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

    aMaze := factory.MakeMaze;

    //---

    r1 := factory.MakeRoom(1);

    r2 := factory.MakeRoom(2);

    //---

    theDoor := factory.MakeDoor(r1,r2);

    //---

    aMaze.AddRoom(r1);

    aMaze.AddRoom(r2);

    //---

    r1.SetSides(North,factory.MakeWall());

    r1.SetSides(East,theDoor);

    r1.SetSides(South,factory.MakeWall());

    r1.SetSides(West,factory.MakeWall());

    //---

    r2.SetSides(North,factory.MakeWall());

    r2.SetSides(East,factory.MakeWall());

    r2.SetSides(South,factory.MakeWall());

    r2.SetSides(West,theDoor);

    //---

    result := aMaze;

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;

 

constructor TMazePrototypeFactory.Create(M: TMaze; W: TWall; R: TRoom; D: TDoor);

begin

    inherited Create;

    //---

    F_PrototypeMaze := M;

    F_PrototypeWall := W;

    F_PrototypeRoom := R;

    F_PrototypeDoor := D;

end;

 

destructor TMazePrototypeFactory.Destroy;

begin

    F_PrototypeMaze.Free;

    F_PrototypeWall.Free;

    F_PrototypeRoom.Free;

    F_PrototypeDoor.Free;

    //---

    inherited;

end;

 

function TMazePrototypeFactory.MakeMaze(): TMaze;

begin

    Result := F_PrototypeMaze.Clone;

end;

 

function TMazePrototypeFactory.MakeRoom(RoomNo: integer): TRoom;

begin

    Result := F_PrototypeRoom.Clone;

    Result.InitializeRoomNo(RoomNo);

end;

 

function TMazePrototypeFactory.MakeWall(): TWall;

begin

    Result := F_PrototypeWall.Clone;

end;

 

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

begin

    Result := F_PrototypeDoor.Clone;

    Result.Initialize(r1,r2);

end;

 

end.

 

unit Unit1;

 

interface

 

uses

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

    StdCtrls,uMazePrototype;

 

type

    TForm1 = class(TForm)

        Button1: TButton;

        ListBox1: TListBox;

        procedure Button1Click(Sender: TObject);

        procedure FormDestroy(Sender: TObject);

        procedure FormCreate(Sender: TObject);

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

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

    private

        FMazeGame: TMazeGame;

        FMazeFactory: TMazeFactory;

        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;

    //---使用基本迷宫组件原型

    //FMazeFactory := TMazePrototypeFactory.Create(TMaze.Create,TWall.Create,TRoom.Create(0),TDoor.Create(nil,nil));

    //---使用有炸弹房及炸弹门的迷宫组件原型

    FMazeFactory := TMazePrototypeFactory.Create(TMaze.Create,TBombedWall.Create(true),TRoomWithaBomb.Create(0),TDoor.Create(nil,nil));

    //---

    FMaze := FMazeGame.CreateMaze(FMazeFactory);

    //---

    FCurRoom := FMaze.RoomNo(1);

    with FCurRoom do

    begin

        Enter;

        ListBox1.Items.Add(StateMsg);

    end;

end;

 

procedure TForm1.FormDestroy(Sender: TObject);

begin

    FMaze.Free;

    FMazeFactory.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;

 

procedure TForm1.Button1Click(Sender: TObject);

var

    Game: TMazeGame;

    MazeFactory: TMazePrototypeFactory;

    Maze: TMaze;

begin

    Game := TMazeGame.Create;

    //---使用基本迷宫组件原型

    MazeFactory := TMazePrototypeFactory.Create(TMaze.Create,TWall.Create,TRoom.Create(0),TDoor.Create(nil,nil));

    //---使用有炸弹房及炸弹门的迷宫组件原型

    //MazeFactory := TMazePrototypeFactory.Create(TMaze.Create,TBombedWall.Create,TRoomWithaBomb.Create(0),TDoor.Create(nil,nil));

    try

        Maze := Game.CreateMaze(MazeFactory);

        //---

        //执行游戏.......

        //---

        Maze.Free;

    finally

        MazeFactory.Free;

        Game.Free;

    end;

end;

 

end.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值