Domanda

Se io implementare un'interfaccia su una classe di base saranno oggetto ereditato dai suoi corsi di sub, so che le funzioni / procedure saranno, ma io sono più interessati a sapere se sarò in grado di lanciare la sottoclasse per l'interfaccia e poi torna alla sua classe originaria.

Quello che sto sperando che posso fare è oggetti passaggio di diverse classi base a una funzione, e quindi nella funzione determin lì tipo e li usa in modo appropriato.

Questo è possibile ed è l'approccio corretto?

Aggiorna

per contribuire a chiarire qualsiasi confusione (o per creare un po 'di più) Ecco quello che vorrei fare (strisce fino al suo nucleo).

Interfaccia

    IMyInterFace = interface
   ['{B7203E50-F5CB-4755-9DB1-FB41B7B192C5}'] 
     function MyFunction: Boolean;
   end;

Classe Base

   type TMyObject = class(TInterfacedObject,IMyInterFace)

Sub Class

  type TSubMyObject = class(TMyObject)

Un'altra classe

  type TMyOtherObject = class(TInterfacedObject,IMyInterFace)

Quindi l'utilizzo

 procedure foo();
   var obj:TSubMyObject;
 begin
      obj := TSubMyObject.Create();
      SendInterfaceObject(obj);
 end;

 procedure bar();
   var obj:TMyOtherObject;
 begin
      obj := TMyOtherObject.Create();
      SendInterfaceObject(obj);
 end;



 procedure SendInterfaceObject(iobj:IMyInterFace);
 begin
     if (iobj is TSubMyObject) then
     begin
        //code here
     end
     else if (iobj is TMyOtherObject) then
     begin
       //code here
     end;
 end;

Aggiorna 2

Ho aggiornato il codice abit così mostrare quello che sono dopo il meglio.

// codice qui sezioni hanno poco a che fare con l'oggetto che sono passati ad esso, ad esempio se questa classe è TAccounts ed è stato passato un oggetto TEmployee può pagare paga settimanale, ma non ci se fosse un TInvoice allora sarebbe controllare per vedere se avesse bisogno di pagare e pagare solo quando la data era 2 giorni prima della linea morta.

il TEmployee / TInvoice può anche provenire da classi laterali fuori chiedendo pagamenti da effettuare.

questo è solo un esempio.

È stato utile?

Soluzione

Sì, l'interfaccia è ereditato dalla sottoclasse.

E 'perfettamente accettabile per lanciare dalla sottoclasse all'interfaccia.

Tuttavia, e le scuse se sto leggendo la tua domanda sbagliata, ma se "e poi di nuovo alla sua classe originaria" mezzi. . .

Hai Interfaccia I, di classe A e di classe B. A implementa I e B eredita una, è forse possibile, ma in realtà non dovrebbe lanciare da A a B.

Modifica

Vuoi andare da B a I e di nuovo a B. . . ma si dispone già di un riferimento a B, se B è quello che si passa alla funzione, quindi non c'è bisogno di lanciare da I a B (a meno che non stavano parlando di un oggetto diverso, allora no, non farlo)

Andando da I a B è lo stesso di andare da A a B, si sta cercando di lanciare la catena di ereditarietà, che è davvero qualcosa che non si dovrebbe fare. Avendo bisogno di fare questo è un codice odore, ti dice che si dovrebbe cercare di risolvere questo problema in modo diverso (possibilmente ridisegnando voi classi (ad esempio l'aggiunta di ulteriori proprietà / metodi a I), o semplicemente decidendo che la funzione sarà solo lavoro con la classe sub - lavorare con 'B' la sottoclasse vi darà accesso a tutti i metodi di a & i)

.

Puoi modificare la tua domanda e aggiungere un po 'di codice di esempio di ciò che si sta cercando di fare?

Modifica 2

 procedure SendInterfaceObject(iobj:IMyInterFace);
 begin
     if (iobj is TSubMyObject) then
     begin
        //code here
     end;
 end;

I 'se' dichiarazione v'è una cattiva idea, e si rompe OO presidi. Se avete bisogno di fare questo allora o

  • La definizione interfaccia è insufficiente, si potrebbe desiderare di aggiungere un Tipo di proprietà all'interfaccia che consente di (se iObj.SomeProperty = 1) allora. . .)
  • L'interfaccia non è semplicemente il corretta soluton a questo problema, e si dovrebbe passare il riferimento come TSubMyObject.

EDIT 3:

@mghie: Sono d'accordo con te, quello che non ho spiegato molto bene era che SomeProperty ha alcuni dati che permette la funzione di succursale, eliminando la dipendenza del tipo di controllo. SomeProperty non dovrebbe 'semplicemente' sostituire il controllo di tipo (per esempio inserendo il nome della classe in una proprietà, quindi controllare il nome della classe) È infatti esattamente lo stesso problema.

V'è una certa differenza essenziale fra sottoclassi che ereditano l'interfaccia. Questa differenza deve essere espresso da uno

  • Esporre qualche elemento di dati che possono poi essere utilizzati nella Brach

es.

if(item.Color = Red) then 
   item.ContrastColor := Blue;
else
   item.ContrastColor := Red;
  • o tramite il polimorfismo per es.

IEmployee definisce un metodo calculatePay, TManager e TWorker attuare IEmployee, ciascuno con logica diversa nei metodi calculatePay.

Se l'intento era quello di fare qualcosa di simile al primo caso, il polimorfismo potrebbe essere eccessivo (il polimorfismo non risolve tutti i problemi).

Modifica 4

Dici "il codice // qui sezioni hanno poco a che fare con l'oggetto che sono passati ad esso..." Mi dispiace, ma questa affermazione non è corretto, se avete bisogno di pagare un impiegato, è necessario conoscere i loro 1) EmployeeCode 2) il loro stipendio dettagli 3) le loro coordinate bancarie, ecc, se si sta caricando una fattura è necessario 1) InvoiceNumber 2) Importo fattura 3) CustomerCode di imputare al ecc. . . questo è un luogo ideale per il polimorfismo .

Diciamo che la funzione di prendere i controlli di interfaccia per vedere se "Account" ha bisogno di fare qualcosa con l'oggetto (ad esempio, corrispondere al dipendente, addebitare una fattura, ecc). Così potremmo chiamare la funzione AccountsCheck. All'interno conti correnti si avrà un pezzo di logica specifica a ciascuna classe sub (a pagare un dipendente, per caricare la fattura...) Questo è un candidato ideale per il polimorfismo.

si interfaccia (o su un'altra interfaccia, o come un metodo virtuale della classe sub) Definire un metodo di "AccountsCheck". Poi ogni classe derivata ottiene la sua propria implementazione di conti correnti.

Il codice si muove fuori della vostra humungous singola funzione AccountsCheck, e in funzioni più piccole relativi a ogni classe secondaria. Questo rende il codice

  • Altroevidente intento (ciascuna classe contiene una logica per AccountsCheck)
  • Sei meno probabilità di rompersi SubClass La logica di B nel fissare qualcosa in AccountsCheck per C
  • E 'più facile capire esattamente ciò SubClass logica AccountsCheck di B è, hai solo controllare 20 linee di codice nella piccola AccountsCheck, non 200 nel AccountsCheck Generale)

Non ci sono più, "buone ragioni" per questo, AIF nyone vuole modificare / inviare commenti si prega di farlo.

Se si trova è necessario condividere una logica tra le implementazioni di AccountsCheck, creare alcune funzioni di utilità, non reimplementare la stessa ruota in ciascuno di essi.

Il polimorfismo è la soluzione al vostro problema.

Altri suggerimenti

Il mio suggerimento sarebbe di non lanciare contro la classe ma invece gettato contro un'altra interfaccia. Cambia la tua TMyOtherObject a:

type
  IOtherObjectIntf = interface
    ['{FD86EE29-ABCA-4D50-B32A-24A7E71486A7}']
  end;

type 
  TMyOtherObject = class(TInterfacedObject,IMyInterFace,IOtherObjectIntf)

e quindi cambiare la vostra altra routine di leggere:

procedure SendInterfaceObject(iobj:IMyInterFace);
begin
  if Supports(iObj,IOtherObjectIntf) then
    begin
      //code here for TMyOtherObject
    end
  else 
    begin
      //code here for other standard implementations
    end;
end;

In questo modo il codice "su misura" per il TMyOtherObject sarebbe anche essere applicato a qualsiasi dei suoi discendenti senza alcuna ulteriore codice personalizzato. L'interfaccia IOtherObjectIntf viene utilizzato come nient'altro che una "Sì, io sono uno di quei" indicatori che consente il codice per diramare correttamente. Certo, i suoi rifiuti, che stabilisce in un altro Guid ... ma ci sono così tanti di loro, chi se ne accorgerebbe? :)

L'interfaccia è ereditato dalle sottoclassi e si può lanciare gli oggetti per l'interfaccia, ma non è sicuro (o consigliato) per lanciare l'interfaccia alla classe. Se avete bisogno di fare questo probabilmente si sta utilizzando interfacce nel modo sbagliato.

Sembra che ci sia qualche dubbio su come la tua domanda è da intendersi, e in effetti nel tuo commento su questa risposta dici che si vuole 'andare da B a I a B'.

Questo non è infatti raccomandato e solo sostenuto utilizzando le informazioni su come le interfacce sono implementati su una classe.

Se ho capito bene allora che cosa si vuole fare è quello di passare un'interfaccia per qualche metodo, e in che modo fare cose diverse a seconda di cosa classe concreta l'interfaccia è stata implementata da. E 'comunque meglio di continuare ad utilizzare le interfacce, una volta che si avvia con loro. È potrebbe lasciare che l'interfaccia dispone di un metodo per restituire la classe che implementa, ma non si deve fare alcuna ipotesi su quale classe l'interfaccia sia implementata in -. Ti costa alcuni dei vantaggi della programmazione contro interfacce

Che cosa si può fare, invece è creare interfacce diverse, e implementare alcuni di loro solo in (alcune) le vostre classi discendenti. Quindi è possibile utilizzare QueryInterface () o supporti () il puntatore passato. Per la vostra classe di base questo tornerà nil , ma per tutte le classi discendenti che implementano l'interfaccia verrà restituito un puntatore che consente di chiamare i metodi solo loro hanno.

Modifica Ad esempio nel OmniThreadLibrary troverete:

IOmniWorker = interface
  ['{CA63E8C2-9B0E-4BFA-A527-31B2FCD8F413}']
  function  GetImplementor: TObject;
  ...
end;

che si potrebbe aggiungere alla vostra interfaccia. Ma ancora una volta, secondo me l'uso di interfacce distinte è molto meglio.

Non si può lanciare un'interfaccia per un oggetto direttamente (non è quello che le interfacce sono destinati), ma a volte è così pratico per essere in grado di farlo, che non si può resistere ...

Se si vuole veramente fare così, è possibile utilizzare l'esempio "IOmniWorker" data dal mghie direttamente in IMyInterface:

IMyInterFace = interface
['{B7203E50-F5CB-4755-9DB1-FB41B7B192C5}'] 
  function MyFunction: Boolean;
  function GetImplementor: TObject;
end;

Le implementazioni di funzione assomigliano che:

function TMyObject.GetImplementor: TObject;
begin
  Result := Self;
end;
function TMyOtherObject.GetImplementor: TObject;
begin
  Result := Self;
end; 

SendInterfaceObject sembra poi così:

procedure SendInterfaceObject(const iobj:IMyInterFace);
begin
  if (iobj.GetImplementor is TSubMyObject) then
  begin
     //code here
  end
  else if (iobj.GetImplementor is TMyOtherObject) then
  begin
    //code here
  end;
end;

Tra l'altro ho aggiunto un (molto) piccola ottimizzazione:. Passando iobj come "const" per la funzione che si può evitare il conteggio dei riferimenti inutili

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top