示例:基于原型的迷宫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.