Soluzione migliore del semplice metodo di produzione quando le implementazioni concrete hanno attributi diversi

1
abstract class Animal {
  function eat() {..}
  function sleep() {..}
  function isSmart()
}

class Dog extends Animal {
  public $blnCanBark;
  function isSmart() {
    return $this->blnCanBark;
  }
}

class Cat extends Animal {
  public $blnCanJumpHigh;
  function isSmart() {
    return $this->blnCanJumpHigh;
  }
}

.. and so on up to 10-20 animals.

Ora ho creato una fabbrica usando un semplice metodo di fabbrica e provo a creare istanze come questa:

class AnimalFactory {
  public static function create($strName) {
       switch($strName) {
          case 'Dog':
            return new Dog();
          case 'Cat':
            return new Cat();
          default:
            break;
       }
  }
}

Il problema è che non posso impostare gli attributi specifici come blnCanBark, blnCanJumpHigh in modo efficiente.

Posso inviarli tutti come parametri aggiuntivi da creare ma questo non si ridimensionerà a più di alcune classi. Inoltre non posso rompere l'ereditarietà perché molte delle funzionalità di base sono le stesse.

C'è un modello migliore per risolvere questo?

    
posta danidacar 30.06.2013 - 18:08
fonte

3 risposte

2

Mi viene in mente la combinazione di fabbrica con Strategia . Ogni animale può supportare la nozione di un insieme di comportamenti che possono essere aggiunti / passati come una matrice di riferimenti all'interfaccia comportamentale o array di istanze di discendenti di una classe di comportamento astratta.

Non pensate che questa sia la strada da percorrere, poiché significa che decidete quali comportamenti devono passare al di fuori della fabbrica e ciò sembra vanificare lo scopo di avere una fabbrica in primo luogo.

Builder sembra specificamente adatto al tuo problema. Ho sempre considerato Builder come un'estensione (non nell'eredità ma nel senso di "prendere un ulteriore passo avanti"). Invece di concentrarsi sull'istanza, si rivolge in particolare alla costruzione di istanze di classi che richiedono più di una semplice chiamata a un costruttore.

Aggiornamento:

Di seguito è riportato un esempio del pattern Builder. Utilizza la sintassi Delphi in quanto è la lingua con cui ho più familiarità, ma mi sono astenuto dall'usare costrutti specifici di Delphi (meta-classi per esempio). Anche il controllo degli errori e la liberazione delle istanze sono stati esclusi dall'esempio.

Si noti che invece di passare al director un'istanza di un builder, è anche possibile passare una stringa e consentire al director di stabilire quale builder istanziare e utilizzare. In tal caso, opterei per un registro di builder nel director e un metodo di registrazione per il director in modo da poter dichiarare e creare builder senza modificare il director.

type
  TBehaviour = class(TObject)
  end;
  TSpeakBehaviour = class(TBehaviour);
  TJumpBehaviour = class(TBehaviour);

  TAnimal = class(TObject)
  public
    procedure AddBehaviour(const aBehaviour: TBehaviour);
  end;

  TCat = class(TAnimal);
  TDog = class(TAnimal);

  TAnimalBuilder = class(TObject)
  public
    procedure BuildBody; virtual; abstract;
    procedure AddBehaviours; virtual; abstract;
    function GetAnimal: TAnimal; virtual; abstract;
  end;

  TCatBuilder = class(TBuilder)
  private
    FCat: TCat;
  public
    procedure BuildBody; override;
    procedure AddBehaviours; override;
    function GetAnimal: TAnimal; override;
  end;

  TDogBuilder = class(TBuilder)
  private
    FDog: TDog;
  public
    procedure BuildBody; override;
    procedure AddBehaviours; override;
    function GetAnimal: TAnimal; override;
  end;

  TAnimalDirector = class(TObject)
  private
    FBuilder: TAnimalBuilder;
  public
    constructor Create(const aBuilder: TAnimalBuilder);
    function BuildAnimal: TAnimal;
  end;

procedure BuildMeAnAnimal;    
var
  Builder: TAnimalBuilder;
  Director: TAnimalDirector;
  Animal: TAnimal;
begin
  Builder := TCatBuilder.Create;
  Director := TAnimalDirector.Create(Builder);
  Animal := Director.BuildAndimal;
end;

//--- Animal Director --------------

constructor TAnimalDirector.Create(const aBuilder: TAnimalBuilder);
begin
  FBuilder := aBuilder;
end;

function TAnimalDirector.BuildAnimal: TAnimal;
begin
  FBuilder.BuildBody;
  FBuilder.AddBehaviours;
  Result := FBuilder.GetAnimal;
end;

//--- Cat Builder --------------

procedure TCatBuilder.BuildBody; 
begin
  FCat := TCat.Create;
end;

procedure TCatBuilder.AddBehaviours; 
begin
  FCat.AddBehaviour(TSpeakBehaviour.Create('miauw'));
  FCat.AddBehaviour(TJumpBehaviour.Create(jaHigh));
end;

function TCatBuilder.GetAnimal: TAnimal; 
begin
  Result := FCat;
end;

//--- Dog Builder --------------

procedure TDogBuilder.BuildBody; 
begin
  FDog := TDog.Create;
end;

procedure TDogBuilder.AddBehaviours; 
begin
  FDog.AddBehaviour(TSpeakBehaviour.Create('woof'));
  FDog.AddBehaviour(TJumpBehaviour.Create(jaMediumHigh));
end;

function TDogBuilder.GetAnimal: TAnimal; 
begin
  Result := FDog;
end;
    
risposta data 30.06.2013 - 19:02
fonte
1

Dipende da perché stai usando lo schema dei metodi di fabbrica.

Se lo scopo principale è quello di essere in grado di restituire una sottoclasse di Cat invece di un semplice Cat , l'utilizzo di più metodi di produzione potrebbe essere più adatto allo scopo, con l'ulteriore vantaggio di rimuovere le "stringhe magiche" passate a create :

class AnimalFactory {
  public static function createCat($canJumpHigh) {
    // in the future, this can be extended to return a HouseCat instead
    return new Cat($canJumpHigh);
  }
  public static function createDog($canBark) {
    return new Dog($canBark);
  }
}
    
risposta data 01.07.2013 - 13:06
fonte
-1

Perché hai public di variabili come blnCanBark e blnCanJumpHigh invece di funzioni set_property corrette.

Se cerco semplicemente di capire il code di cui sopra, penso che ci dovrebbe essere una funzione set_properties() con un numero di parametri non elencato o migliore set_properties(array $properties) .

Potrebbe non essere gestibile con semplici funzioni e sarei andato per abstract class e interface per una logica più complessa.

Ma se la semplicità è desiderabile allora set_properties(array $properties) è la soluzione.

    
risposta data 01.07.2013 - 11:39
fonte

Leggi altre domande sui tag