示例:基于原型的迷宫2
实现:
你可以用MazeFactory来生成你需要的原型;例如,你可以提供名字#room来创建一个房间。MazeFactory有一个将名字映射为原型的字典partCatalog。partCatalog的Make方法可以根据名称创建原型。
代码:
unit uMazePrototype2;
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;
function Clone: TMapSite; 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: TMapSite; 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);
//---
function HasBomb(): Boolean;
function Enter: Boolean; override;
procedure Initialize(Bombed: boolean);
function Clone: TMapSite; override;
end;
{墙壁}
TWall = class(TMapSite)
public
function Enter: Boolean; override;
function Clone: TMapSite; override;
end;
TBombedWall = class(TWall)
private
FBomb: boolean;
public
constructor Create(Bombed: boolean = false);
//---
function Enter: Boolean; override;
procedure Initialize(Bombed: boolean);
function Clone: TMapSite; 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: TMapSite; 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;
function Clone: TMaze; virtual;
end;
TPartInfo = record
PartName: string;
PartTemplate: TMapSite;
end;
PPartInfo = ^TPartInfo;
TPartCatalog = class
private
FDataList: TList;
function GetParts(PartName: string): TMapSite;
procedure SetParts(PartName: string; const Value: TMapSite);
procedure Clear;
function Find(const PartName: string): PPartInfo;
procedure Add(const PartName: string; const PartTemplate: TMapSite);
public
constructor Create;
destructor Destroy; override;
//---
property Parts[PartName: string]: TMapSite read GetParts write SetParts; default;
end;
{迷宫构件工厂}
TMazeFactory = class
private
FPartCatalog: TPartCatalog;
public
constructor Create;
destructor Destroy; override;
//---
procedure AddPart(PartTemplate: TMapSite; const PartName: string);
function Make(const PartName: string): TMapSite; virtual;
function MakeMaze(): TMaze; virtual;
end;
{迷宫游戏}
TMazeGame = class
public
function CreateMaze(factory: TMazeFactory): TMaze;
end;
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.GetSides(Direction: TDirection): TMapSite;
begin
Result := FSides[Direction];
end;
procedure TRoom.SetSides(Direction: TDirection; const Value: TMapSite);
begin
FSides[Direction] := Value;
end;
function TRoom.Enter: Boolean;
begin
self.StateMsg := format('进入房间%d', [FRoomNumber]);
Result := true;
end;
procedure TRoom.InitializeRoomNo(RoomNo: integer);
begin
FRoomNumber := RoomNo;
end;
function TRoom.Clone: TMapSite;
begin
result := TRoom.Create(self.RoomNumber);
end;
function TWall.Enter: Boolean;
begin
self.StateMsg := '碰到墙';
Result := false;
end;
function TWall.Clone: TMapSite;
begin
Result := TWall.Create;
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.Clone: TMapSite;
begin
result := TDoor.Create(nil,nil);
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;
//---
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 TBombedWall.Clone: TMapSite;
var
Wall: TBombedWall;
begin
Wall := TBombedWall.Create;
Wall.Initialize(self.FBomb);
//---
Result := Wall;
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;
function TRoomWithABomb.Clone: TMapSite;
var
Room: TRoomWithaBomb;
begin
Room := TRoomWithaBomb.Create(self.RoomNumber);
Room.Initialize(self.HasBomb);
//---
result := Room;
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;
Door: TDoor;
begin
//---建构一个maze,有两个Room,一个Door,六面Wall
aMaze := factory.MakeMaze;
//---
r1 := factory.Make('#Room') as TRoom;
r1.InitializeRoomNo(101);
r2 := factory.Make('#Room') as TRoom;
r2.InitializeRoomNo(201);
//---
Door := factory.Make('#Door') as TDoor;
Door.Initialize(r1,r2);
//---
aMaze.AddRoom(r1);
aMaze.AddRoom(r2);
//---
r1.SetSides(North,factory.Make('#Wall'));
r1.SetSides(East,Door);
r1.SetSides(South,factory.Make('#Wall'));
r1.SetSides(West,factory.Make('#Wall'));
//---
r2.SetSides(North,factory.Make('#Wall'));
r2.SetSides(East,factory.Make('#Wall'));
r2.SetSides(South,factory.Make('#Wall'));
r2.SetSides(West,Door);
//---
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 TPartCatalog.Create;
begin
inherited;
//---
FDataList := TList.Create;
end;
destructor TPartCatalog.Destroy;
begin
Clear;
FDataList.Free;
//---
inherited;
end;
procedure TPartCatalog.Clear;
var
i: Integer;
pData: PPartInfo;
begin
with FDataList do
begin
for i := 0 to Count - 1 do
begin
pData := Items[i];
pData.PartTemplate.Free;
dispose(pData);
end;
//---
Clear;
end;
end;
procedure TPartCatalog.Add(const PartName: string; const PartTemplate:
TMapSite);
var
pData: PPartInfo;
begin
new(pData);
//---
pData.PartName := PartName;
pData.PartTemplate := PartTemplate;
//---
FDataList.Add(pData);
end;
function TPartCatalog.Find(const PartName: string): PPartInfo;
var
i: integer;
pData: PPartInfo;
begin
with FDataList do
begin
for i := 0 to Count - 1 do
begin
pData := Items[i];
if pData.PartName = PartName then
begin
Result := pData;
exit;
end;
end;
end;
//---
Result := nil;
end;
function TPartCatalog.GetParts(PartName: string): TMapSite;
var
pData: PPartInfo;
begin
pData := Find(PartName);
if pData = nil then
Result := nil
else
Result := pData.PartTemplate;
end;
procedure TPartCatalog.SetParts(PartName: string; const Value: TMapSite);
var
pData: PPartInfo;
begin
pData := Find(PartName);
if pData = nil then
Add(PartName,Value)
else
begin
pData.PartTemplate.Free;
pData.PartTemplate := Value;
end;
end;
constructor TMazeFactory.Create;
begin
FPartCatalog := TPartCatalog.Create;
end;
destructor TMazeFactory.Destroy;
begin
FPartCatalog.Free;
//---
inherited;
end;
procedure TMazeFactory.AddPart(PartTemplate: TMapSite; const PartName: string);
begin
FPartCatalog.Parts[PartName] := PartTemplate;
end;
function TMazeFactory.Make(const PartName: string): TMapSite;
begin
Result := FPartCatalog[PartName].Clone;
end;
function TMazeFactory.MakeMaze: TMaze;
begin
Result := TMaze.Create;
end;
end.
unit Unit1;
interface
uses
Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Dialogs,
StdCtrls,uMazePrototype2;
type
TForm1 = class(TForm)
ListBox1: TListBox;
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 := TMazeFactory.Create;
with FMazeFactory do
begin
//---使用基本迷宫组件原型
AddPart(TDoor.Create(nil,nil),'#Door');
AddPart(TWall.Create,'#Wall');
AddPart(TRoom.Create(0),'#Room');
//---使用有炸弹房及炸弹门的迷宫组件原型
{AddPart(TDoor.Create(nil,nil),'#Door');
AddPart(TBombedWall.Create(true),'#Wall');
AddPart(TRoomWithaBomb.Create(0),'#Room');}
end;
//---
FMaze := FMazeGame.CreateMaze(FMazeFactory);
//---
FCurRoom := FMaze.RoomNo(101);
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;
end.