构建支持继承的对象工厂

时间:2013-05-26 14:25:04

标签: delphi delphi-7

我想创建一个对象工厂武器类,它可以实现我分配给它的无限量的射弹类。武器类和射弹类被用作“抽象”类(我知道Delphi 7不支持抽象类)。

现在我不知道如何实现正确实现projectileType的Weapon.shoot()(SimpleWeapon类使用SimpleProjectile作为projectileType)

procedure Weapon.shoot();
var g : Projectile;
begin
        // instanciate ammo type
        g := projectileType.create();
        g.init();
end; 

首先,projectileType.create()返回一个我无法直接分配给Projectile变量的TObject,并且通过projectileType(g)转换TObject也不起作用。

其次,我怎样才能实现g将被转换为projectileType,以便我可以使用该特定类的init()方法而不是它的父类(Projectile)?

UML diagram

4 个答案:

答案 0 :(得分:3)

声明类类型SimpleProjectileClass = class of SimpleProjectile。然后使projectType返回该类型:projectType: SimpleProjectileClass

答案 1 :(得分:2)

武器不需要知道它可以发射什么样的射弹。它只应该知道,射击/射击。

因此,实施武器战略将是最好的建议

type
  TWeapon = class;

  IWeaponStrategy = interface
    ['{B47CD780-906D-4515-BDA6-1EC8118BC29E}']
    procedure Shoot( AWeapon : TWeapon );
  end;

  TWeapon = class
  private
    FStrategy : IWeaponStrategy;
  public
    procedure Shoot;

    procedure SetStrategy( AStrategy : IWeaponStrategy );
  end;

implementation

{ TWeapon }

procedure TWeapon.SetStrategy( AStrategy : IWeaponStrategy );
begin
  FStrategy := AStrategy;
end;

procedure TWeapon.Shoot;
begin
  FStrategy.Shoot( Self );
end;

要获得武器发射子弹,请构建一个WeaponBulletStrategy

type
  TWeaponBulletStrategy = class( TInterfacedObject, IWeaponStrategy )
  protected
    procedure Shoot( AWeapon : TWeapon );
  end;

implementation

procedure TWeaponBulletStrategy.Shoot( Aweapon : TWeapon );
begin
  // Build the Bullet Instance and initialize
end;

现在你可以轻松改变武器射击行为或实施新的射弹类而无需改变武器类别。

答案 2 :(得分:2)

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type

  TBaseProjectile=Class
     Procedure Init;
  End;

  TProjectile_1=Class(TBaseProjectile)
  End;

  TProjectile_2=Class(TBaseProjectile)
  End;

  TBaseProjectile_Class=Class of TBaseProjectile;
  TProjectile_1Class=Class of TProjectile_1;
  TProjectile_2Class=Class of TProjectile_2;

  TBaseWappon=Class
    FClassRef:TBaseProjectile_Class;
    protected
    public
    Constructor Create(ProjektileClass:TBaseProjectile_Class);virtual;
    Procedure Shoot;
  End;

  TWappon_1=Class(TBaseWappon)
    public
  End;

  TWappon_2=Class(TBaseWappon)
    public
  End;


  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TBaseProjectile.Init;
begin
    Showmessage(Classname);
end;



{ TBaseWappon }

constructor TBaseWappon.Create(ProjektileClass:TBaseProjectile_Class);
begin
   FClassRef:=ProjektileClass;
end;

procedure TBaseWappon.Shoot;
begin
  With FClassRef.Create do
    begin
    init;
    Free;
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  With TWappon_1.create(TProjectile_1) do
    begin
      Shoot;
      Free;
    end;
  With TWappon_2.create(TProjectile_2) do
    begin
      Shoot;
      Free;
    end;

end;

end.

答案 3 :(得分:0)

以下是我对此问题的看法:

type
  TProjectile = class
  protected
    function GetDamage: Integer; virtual; abstract;
    function GetPenetration: Double; virtual; abstract;
  public
    procedure DoExtenalBallistics(WindSpeed, WindDirection: Real); virtual;
    procedure DoInternalBallistics(Acceleration: Real); virtual;
    procedure DoTerminalBallistics(TargetArmor: Real); virtual;
    property Damage: Integer read GetDamage;
    /// <summary>
    /// Sorry, can not resist ;-)
    /// </summary>
    property Penetration: Double read GetPenetration;
  end;

procedure TProjectile.DoExtenalBallistics(WindSpeed, WindDirection: Real);
begin
  // TODO : do parabolic travel within the gravity pull of Earth, affected by wind
end;

procedure TProjectile.DoInternalBallistics(Acceleration: Real);
begin
  // TODO : do gain kinetic energy in the acceleration chamber
end;

procedure TProjectile.DoTerminalBallistics(TargetArmor: Real);
begin
  // TODO : TargetHitPoitns - (Damage / (TargetArmor - Penetration)) or something
end;

type
  TProjectileType = class of TProjectile;

  TFirearm = class
  protected
    function GetSupportedProjectile: TProjectileType; virtual; abstract;
  public
    function CanShoot(Projectile: TProjectile): Boolean; virtual;
    procedure Shoot;
    property SupportedProjectile: TProjectileType read GetSupportedProjectile;
  end;

function TFirearm.CanShoot(Projectile: TProjectile): Boolean;
begin
  Result := Projectile is SupportedProjectile;
end;

procedure TFirearm.Shoot;
var
  Projectile: TProjectile;
begin
  Projectile := SupportedProjectile.Create;
  Projectile.DoInternalBallistics(CardridgePowder);
  Projectile.DoExtenalBallistics(Theatre.Wind.Speed, Theatre.Wind.Direction);
  Projectile.DoTerminalBallistics(Targer.Armors);
  { ... }
  { ofc is CanShoot returns false, then http://i192.photobucket.com/albums/z96/M4builder/destructivetestedbarrrels01copy.jpg and Firearm.Owner.Fingers - 5 }
end;

type
  TSmoothBoreProjectile = class(TProjectile)
  end;

  TBuckshot = class(TSmoothBoreProjectile)
  end;

  Flechette = class(TSmoothBoreProjectile)
  end;

  TShotgun = class(TFirearm)
  protected
    function GetSupportedProjectile: TProjectileType; override;
  end;

function TShotgun.GetSupportedProjectile: TProjectileType;
begin
  Result := TSmoothBoreProjectile;
end;

为简洁起见,我省略了几个必需的覆盖(模型比原始类图大得多),并用伪实际射弹应该做的伪替换你提出的Init方法。