Perché gli oggetti sono istanziati in questo modo?

7

Alcune volte vedo un oggetto istanziato in questo modo.

ICustomer oCustomer = new Customer

Ovvio, ma la classe Customer sta implementando l'interfaccia ICustomer in questo esempio. Ci sono dei vantaggi nell'istanziare un oggetto in questo modo? Dalla mia comprensione, se una classe nell'implementazione di un'interfaccia sta già seguendo il contratto. Qual è il nome di questa pratica e c'è uno svantaggio nell'istanziare un oggetto "normale" (come faccio di solito) in modo? So che ci sono già alcune domande esistenti, ma non mettono in evidenza i vantaggi e gli svantaggi

Istanza di interfaccia con istanza della classe

Customer oCustomer = new Customer
    
posta Howls Hagrid 02.04.2015 - 02:09
fonte

5 risposte

18

C'è una distinzione importante molto che penso tu stia ignorando.

Il codice che hai fornito è per tre cose: la dichiarazione di una variabile, l'istanziazione di un oggetto e l'inizializzazione di quella variabile con quell'oggetto.

Non c'è implementazione di interfaccia qui . Customer deve implementare ICustomer (o fare uno o due altri trucchi) per fare in modo che compili con successo , ma non ci sono implementazioni effettive in corso qui.

Ora che la terminologia è fuori mano, concentriamoci sulla carne del problema: è meglio?

Questo genere di cose rientra nel regno di programma a un'interfaccia . Quando dichiari la tua variabile, devi dargli un tipo. Quel tipo dovrebbe definire il tipo di cose che la variabile può fare. È un contratto che fai con i futuri programmatori (o te stesso) che oCustomer può fare solo questo genere di cose. Dovresti decidere questo contratto a prescindere della fonte che usi per inizializzare la variabile.

Se questa variabile richiede solo l'interfaccia, quindi l'utilizzo dell'interfaccia come tipo definisce chiaramente cosa può (e non può) fare. Se questa veramente deve essere completamente Customer , allora controlla che veramente sia necessario. Se lo fai, quindi vai avanti e rendilo un Customer . Ciò fornisce una chiara comunicazione ai futuri programmatori (o a te) che la variabile deve essere di quel tipo. E se la variabile è solo un segnaposto per il tuo inizializzatore; se il suo tipo non ha importanza, usa var .

I vantaggi della programmazione di un'interfaccia sono descritti in modo approfondito nel link sopra, ma si riducono a consentire di essere flessibili. Il software cambia inevitabilmente. Mediante la codifica su un'interfaccia, consenti a questo codice di rimanere beatamente inconsapevole quando Customer cambia, oppure se hai bisogno di domani per inizializzare oCustomer con VipCustomer .

Questo genere di cose si applica in molti punti durante la programmazione, non solo le dichiarazioni variabili.

    
risposta data 02.04.2015 - 02:43
fonte
10

Finché il tuo codice appare così semplice

void ExampleFunc()
{
   ICustomer oCustomer = new Customer();
   oCustomer.Method1OfICustomer();
   oCustomer.Method2OfICustomer();
   // ...
}

c'è una differenza semantica no : puoi scambiare "Cliente" con "Cliente" e il comportamento rimarrà identico. In questo esempio, tuttavia, potrebbe soddisfare già alcuni scopi documentali. La prima riga può esprimere la decisione progettuale del programmatore per evitare di aggiungere chiamate come

   oCustomer.MethodNotPartOfICustomer();

in futuro, ma in effetti, quando questo tipo di chiamata è necessario in seguito, si potrebbe anche cambiare la "nuova" linea, o aggiungere il metodo mancante a ICustomer più tardi.

In effetti, l'uso delle interfacce, come suggerisce il termine, comincerà a dare più senso quando è coinvolto un vero "interfacciamento": l'interfaccia tra funzioni, classi, componenti. Ad esempio, la funzione

void ExampleFunc(ICustomer oCustomer)
{
   oCustomer.Method1OfICustomer();
   oCustomer.Method2OfICustomer();
   // ...
}

è probabilmente più generico di

void ExampleFunc(Customer oCustomer)
{
   oCustomer.Method1OfICustomer();
   oCustomer.Method2OfICustomer();
   // ...
}

poiché funzionerà con qualsiasi classe che implementa l'interfaccia ICustomer , non solo Customer . Ad esempio, per scopi di test si potrebbe pensare di passare un "CustomerMock" nella funzione, qualcosa che non si può fare con la seconda variante.

    
risposta data 02.04.2015 - 08:38
fonte
2

Il modo in cui lo crei non è migliore o peggiore in alcun modo.

Pensaci:

O si impostano oggetti o si ottengono oggetti (cioè passandoli in un costruttore, metodo o restituendoli da un metodo).

In entrambi i casi fintanto che stai fornendo un'interfaccia per impostare e ottenere puoi rinnovare il tuo oggetto nel modo desiderato, perché in entrambi i casi sarai in grado di sfruttare il polimorfismo, l'astrazione e l'incapsulamento - e questo è l'intero punto dell'intero principio program to interface not to implementation , che è la tua domanda principale.

    
risposta data 02.04.2015 - 02:46
fonte
1

In C # non c'è molta differenza.

La differenza sta in ciò che puoi chiamare sul riferimento che hai creato.

Questo codice

ICustomer customer = new Customer();

crea un riferimento di interfaccia chiamato customer all'istanza creata dalla chiamata new Customer(); .

Questo codice

Customer customer = new Customer();

crea un riferimento oggetto nome customer all'istanza creata dalla chiamata new Customer(); .

Sebbene entrambi i riferimenti facciano riferimento a un'istanza della classe Customer , sono diversi. Il riferimento all'interfaccia offre solo ciò che è definito nell'interfaccia ICustomer , indipendentemente da cos'altro renderà disponibile la classe Customer . Utilizzando il riferimento all'oggetto puoi utilizzare tutto ciò che la classe Customer deve offrire.

Usando un riferimento all'interfaccia si dovrebbe ricondurlo alla classe di implementazione per utilizzare la roba "aggiuntiva" offerta dalla classe. Qualcosa che fai veramente non . Dopo tutto, quando si passa un riferimento all'interfaccia non si può essere certi di quale classe è stata utilizzata per creare un'istanza dell'oggetto a cui fa riferimento. C'è davvero un'eccezione a questa regola: quando hai appena creato il riferimento all'interfaccia e hai bisogno di usare metodi che non vuoi mettere nell'interfaccia perché sono necessari solo durante la costruzione / costruzione / inizializzazione dell'istanza. E anche questo dovrebbe essere un caso raro.

Per C # è tutto.

Per le lingue in cui lo sviluppatore è responsabile della gestione a vita esplicita degli oggetti che sono istanziati (dovendo liberare esplicitamente ciascun oggetto), ci può essere un'enorme differenza tra i due modi di fare riferimento all'oggetto istanziato.

In Delphi, ad esempio, gli oggetti interfacciati (istanze di una classe che implementa un'interfaccia) sono conteggiati come riferimento per impostazione predefinita e vengono "automaticamente" liberati quando il loro conteggio dei riferimenti scende a zero. Il conteggio dei riferimenti viene incrementato e decrementato dalle chiamate _AddRef e _Release che vengono aggiunte automaticamente al tuo codice dal compilatore.

In Delphi non si vuole veramente mai creare un riferimento ad un oggetto di una classe interfacciata. Ciò significa che il compilatore non inserirà la chiamata _AddRef che incrementa il conteggio dei riferimenti a 1 all'istanziazione. Il che significa che il conteggio dei riferimenti è troppo basso. Ciò significa che l'istanza verrà liberata troppo presto.

Ecco perché il consiglio per linguaggi come Delphi è di non mescolare mai riferimenti di interfaccia e oggetto e usare sempre riferimenti di interfaccia per classi interfacciate .

    
risposta data 03.04.2015 - 16:08
fonte
-6

Questa domanda è al centro di ciò che Object-Orientation significa. In parole povere, in un linguaggio come Java, C # o Visual Basic.NET, class es (e struct s) definiscono Tipi di dati astratti e interface s definiscono Oggetti . Non appena hai un class o struct come un tipo (cioè il tipo di una variabile locale, un campo, una proprietà o un parametro del metodo, un tipo di ritorno del metodo, un argomento di tipo a una classe o un'interfaccia generica, il target di un operatore di cast o l'argomento di un operatore instanceof ), non stai facendo programmazione orientata agli oggetti.

L'unica cosa che puoi usare per un tipo è un interface , non puoi usare class es o struct s (e certamente non i primitivi in un linguaggio come Java che li ha). L'unico posto in cui è consentito un class o struct , è accanto all'operatore new : class es e struct s sono solo fabbriche per oggetti.

Questo è spiegato molto meglio di quanto potrei mai sperare di ottenere in Informazioni sull'astrazione dei dati , Rivisitato da William R. Cook . Utilizza Java per gli esempi, ma si applica altrettanto bene a C # o qualsiasi altra lingua.

Quindi, la risposta semplice è: se non stavi creando un oggetto in questo modo, non sarebbe essere un oggetto, ma un'istanza di un tipo di dati astratto.

    
risposta data 02.04.2015 - 08:54
fonte

Leggi altre domande sui tag