Domanda

Un'inciampa su questa frase durante la lettura di modelli di progettazione.

Ma io non lo capisco, qualcuno potrebbe spiegare questo per me?

È stato utile?

Soluzione

  

Interfacce sono solo i contratti o le firme e non sanno   niente di implementazioni.

Codifica contro mezzi di interfaccia, il codice del client tiene sempre un oggetto di interfaccia che viene fornita da una fabbrica. Qualsiasi istanza restituita dalla fabbrica sarebbe di interfaccia tipo che qualsiasi classe candidato fabbrica deve essere implementato. In questo modo il programma client non è preoccupato per l'attuazione e la firma di interfaccia determina ciò che tutte le operazioni possono essere eseguite. Questo può essere usato per modificare il comportamento di un programma in fase di esecuzione. Inoltre, consente di scrivere programmi molto meglio dal punto di vista della manutenzione.

Ecco un esempio di base per voi.

public enum Language
{
    English, German, Spanish
}

public class SpeakerFactory
{
    public static ISpeaker CreateSpeaker(Language language)
    {
        switch (language)
        {
            case Language.English:
                return new EnglishSpeaker();
            case Language.German:
                return new GermanSpeaker();
            case Language.Spanish:
                return new SpanishSpeaker();
            default:
                throw new ApplicationException("No speaker can speak such language");
        }
    }
}

[STAThread]
static void Main()
{
    //This is your client code.
    ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
    speaker.Speak();
    Console.ReadLine();
}

public interface ISpeaker
{
    void Speak();
}

public class EnglishSpeaker : ISpeaker
{
    public EnglishSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak English.");
    }

    #endregion
}

public class GermanSpeaker : ISpeaker
{
    public GermanSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak German.");
    }

    #endregion
}

public class SpanishSpeaker : ISpeaker
{
    public SpanishSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak Spanish.");
    }

    #endregion
}

alt text

  

Questo è solo un esempio di base e   effettiva spiegazione del principio è   oltre la portata di questa risposta.

Modifica

Ho aggiornato l'esempio precedente e ha aggiunto una classe astratta base del diffusore. In questo aggiornamento, ho aggiunto una funzione per tutte le Spakers a "SayHello". Tutti altoparlante parlare "Ciao Mondo". Ecco, questo è una caratteristica comune con funzione simile. Fare riferimento al diagramma di classe e troverete che Speaker classe astratta implementare l'interfaccia iSpeaker e segna lo Speak () come astratti che significa che l'attuazione di ogni altoparlante è responsabile per l'attuazione del metodo di Speak poiché varia da un altoparlante all'altro. Ma tutto altoparlante dire "Ciao" all'unanimità. Così nella classe Speaker astratta definiamo un metodo che dice "Ciao Mondo" e ogni implementazione Speaker deriverà il metodo SayHello.

Si consideri un caso in cui non può SpanishSpeaker Say Ciao quindi in questo caso è possibile eseguire l'override del metodo SayHello per lo spagnolo altoparlante e rilancio corretta eccezione.

  

Si prega di notare che, abbiamo   Non apportate modifiche all'interfaccia   ISpeaker. E il codice cliente e   SpeakerFactory anche rimangono inalterati   invariato. E questo è ciò che otteniamo da Programmazione-to-Interface .

E abbiamo potuto ottenere questo comportamento con la semplice aggiunta di una base di classe astratta Speaker e qualche modifica minore in Ogni implementazione lasciando così il programma originale invariata. Questa è una caratteristica desiderata di qualsiasi applicazione e rende l'applicazione facile manutenzione.

public enum Language
{
    English, German, Spanish
}

public class SpeakerFactory
{
    public static ISpeaker CreateSpeaker(Language language)
    {
        switch (language)
        {
            case Language.English:
                return new EnglishSpeaker();
            case Language.German:
                return new GermanSpeaker();
            case Language.Spanish:
                return new SpanishSpeaker();
            default:
                throw new ApplicationException("No speaker can speak such language");
        }
    }
}

class Program
{
    [STAThread]
    static void Main()
    {
        //This is your client code.
        ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
        speaker.Speak();
        Console.ReadLine();
    }
}

public interface ISpeaker
{
    void Speak();
}

public abstract class Speaker : ISpeaker
{

    #region ISpeaker Members

    public abstract void Speak();

    public virtual void SayHello()
    {
        Console.WriteLine("Hello world.");
    }

    #endregion
}

public class EnglishSpeaker : Speaker
{
    public EnglishSpeaker() { }

    #region ISpeaker Members

    public override void Speak()
    {
        this.SayHello();
        Console.WriteLine("I speak English.");
    }

    #endregion
}

public class GermanSpeaker : Speaker
{
    public GermanSpeaker() { }

    #region ISpeaker Members

    public override void Speak()
    {
        Console.WriteLine("I speak German.");
        this.SayHello();
    }

    #endregion
}

public class SpanishSpeaker : Speaker
{
    public SpanishSpeaker() { }

    #region ISpeaker Members

    public override void Speak()
    {
        Console.WriteLine("I speak Spanish.");
    }

    public override void SayHello()
    {
        throw new ApplicationException("I cannot say Hello World.");
    }

    #endregion
}

alt text

Altri suggerimenti

Pensate a un'interfaccia come un contratto tra un oggetto ed i suoi clienti. Cioè i specifica di interfaccia le cose che un oggetto può fare, e le firme per l'accesso queste cose.

Le implementazioni sono i comportamenti effettivi. Diciamo per esempio, di avere un metodo sort (). È possibile implementare QuickSort o MergeSort. Che non dovrebbe importare al codice client chiamante sorta fintanto che l'interfaccia non cambia.

Biblioteche come l'API Java e .NET Framework fanno un largo uso di interfacce perché milioni di programmatori utilizzano gli oggetti forniti. I creatori di queste librerie devono essere molto attenti che non cambiano l'interfaccia per le classi in queste librerie, perché interesserà tutti i programmatori che utilizzano la libreria. D'altra parte si può modificare l'implementazione tanto quanto simile.

Se, come programmatore, è il codice contro l'implementazione poi, non appena cambia il codice smette di funzionare. Quindi, pensare ai vantaggi dell'interfaccia in questo modo:

  1. nasconde le cose che è necessario conoscere rendendo l'oggetto più semplice da usare.
  2. fornisce il contratto di come l'oggetto si comporterà in modo da poter dipendere da quello

Ciò significa che si dovrebbe cercare di scrivere il codice in modo che utilizza un'astrazione (classe o un'interfaccia astratta) invece della realizzazione diretta.

normalmente l'applicazione viene iniettato nel codice attraverso il costruttore o una chiamata di metodo. Quindi, il codice conosce l'interfaccia o classe astratta e può chiamare tutto ciò che viene definito in questo contratto. Come oggetto reale (implementazione della classe interfaccia / abstract) viene utilizzato, le chiamate operano sull'oggetto.

Questo è un sottoinsieme della Liskov Substitution Principle (LSP), L della principi SOLID .

Un esempio in .NET sarebbe quello di codice con IList invece di List o Dictionary, quindi è possibile utilizzare qualsiasi classe che implementa IList intercambiabile nel codice:

// myList can be _any_ object that implements IList
public int GetListCount(IList myList)
{
    // Do anything that IList supports
    return myList.Count();
}

Un altro esempio dalla libreria di classi base (BCL) è la ProviderBase classe astratta - questo fornisce alcune infrastrutture, e altrettanto importante significa tutte le implementazioni del provider possono essere usati in modo intercambiabile voi se il codice contro di essa

.

Se si dovesse scrivere un Class Car nell'era combustione-Car, poi c'è una grande occasione si implementa oilChange () come parte di questa classe. Ma, quando si introducono le auto elettriche, si sarebbe in difficoltà in quanto non v'è alcun cambio olio coinvolti per queste vetture, e non L'implementazione.

La soluzione al problema è quello di avere un performMaintenance () Interfaccia in auto di classe e di nascondere i dettagli all'interno adeguata attuazione. Ogni tipo di auto avrebbe fornito la propria implementazione per performMaintenance (). Come proprietario di tutto una macchina si ha a che fare con è performMaintenance () e non preoccuparsi di adattare quando c'è un cambiamento.

class MaintenanceSpecialist {
    public:
        virtual int performMaintenance() = 0;
};

class CombustionEnginedMaintenance : public MaintenanceSpecialist {
    int performMaintenance() { 
        printf("combustionEnginedMaintenance: We specialize in maintenance of Combustion engines \n");
        return 0;
    }
};

class ElectricMaintenance : public MaintenanceSpecialist {
    int performMaintenance() {
        printf("electricMaintenance: We specialize in maintenance of Electric Cars \n");
        return 0;
    }
};

class Car {
    public:
        MaintenanceSpecialist *mSpecialist;
        virtual int maintenance() {
            printf("Just wash the car \n");
            return 0;
        };
};

class GasolineCar : public Car {
    public: 
        GasolineCar() {
        mSpecialist = new CombustionEnginedMaintenance();
        }
        int maintenance() {
        mSpecialist->performMaintenance();
        return 0;
        }
};

class ElectricCar : public Car {
    public: 
        ElectricCar() {
             mSpecialist = new ElectricMaintenance();
        }

        int maintenance(){
            mSpecialist->performMaintenance();
            return 0;
        }
};

int _tmain(int argc, _TCHAR* argv[]) {

    Car *myCar; 

    myCar = new GasolineCar();
    myCar->maintenance(); /* I dont know what is involved in maintenance. But, I do know the maintenance has to be performed */


    myCar = new ElectricCar(); 
    myCar->maintenance(); 

    return 0;
}

spiegazione supplementare: Tu sei un proprietario di auto che possiede più auto. Si può tagliare fuori il servizio che si desidera in outsourcing. Nel nostro caso vogliamo esternalizzare il lavoro di tutte le automobili di manutenzione.

  1. identificare il contratto (Interface) che vale per tutte le auto e fornitori di servizi.
  2. I fornitori di servizi uscire con un meccanismo per fornire il servizio.
  3. non vogliono preoccuparsi di associare il tipo di auto con il fornitore di servizi. Basta specificare quando si desidera la manutenzione programma e richiamarlo. società di servizi adeguata dovrebbe saltare ed eseguire i lavori di manutenzione.

    approccio alternativo.

  4. identificare il lavoro (può essere una nuova interfaccia Interface) che vale per tutte le vostre auto.
  5. uscire con un meccanismo per fornire il servizio. In sostanza si sta per fornire l'implementazione.
  6. si richiama il lavoro e farlo da soli. Qui si sta andando a fare il lavoro di lavori di manutenzione adeguata.

    Qual è il lato negativo del 2 ° approccio? Potrebbe non essere l'esperto a trovare il modo migliore per fare la manutenzione. Il vostro compito è quello di guidare l'auto e godere. Non per essere nel business della manutenzione.

    Qual è il lato negativo del primo approccio? V'è il sovraccarico di trovare una società ecc Se non sei un società di noleggio auto, potrebbe non essere vale la pena.

Questa affermazione è di circa l'accoppiamento. Una ragione potenziale di utilizzo programmazione orientata agli oggetti è riutilizzo. Così, per esempio, si può dividere l'algoritmo tra due oggetti collaboratore A e B. Questo potrebbe essere utile per la creazione successiva di un altro algoritmo, che potrebbe riutilizzare uno o l'altro dei due oggetti. Tuttavia, quando gli oggetti comunicano (Messaggi inviati - Metodi di chiamata), creano dipendenze tra l'altro. Ma se si desidera utilizzare uno senza l'altro, è necessario specificare che cosa dovrebbe fare qualche altro oggetto C fare per oggetto A se sostituiamo B. Tali denominazioni sono chiamati interfacce. Questo consente oggetto A di comunicare senza cambiamento con diverso oggetto basandosi sull'interfaccia. La dichiarazione che lei ha citato dice che se si prevede di riutilizzare una parte di un algoritmo (o più in generale un programma), è necessario creare le interfacce e contare su di loro, per cui si potrebbe modificare l'implementazione concreta in qualsiasi momento senza modificare gli altri oggetti se si utilizza il interfaccia dichiarato.

Come altri hanno detto, significa che il codice chiamante deve sapere solo circa un genitore astratta, non la classe effettiva attuazione che farà il lavoro.

Che aiuta a capire questo è il perché si dovrebbe sempre programmazione di un'interfaccia. Ci sono molte ragioni, ma due dei più facili da spiegare sono

1) Testing.

dire Let ho tutta la mia codice del database in una classe. Se il mio programma sa circa la classe concreta, posso testare solo il mio codice in realtà eseguendo contro quella classe. Sto usando -.> Per significare "parla"

WorkerClass -> DALClass Tuttavia, aggiungiamo un'interfaccia per il mix.

WorkerClass -> IDAL -.> DALClass

Quindi, gli attrezzi DALClass l'interfaccia IDAL, e la classe lavoratrice chiama solo attraverso questo.

Ora, se vogliamo le prove di scrittura per il codice, che potrebbe invece fare una semplice classe che solo agisce come un database.

WorkerClass -> IDAL -.> IFakeDAL

2) Riutilizzo

Seguendo l'esempio di cui sopra, diciamo che vogliamo passare da SQL Server (che i nostri usi DALClass concreti) per MonogoDB. Questo richiederà grande opera, ma non se abbiamo programmato a un'interfaccia. In questo caso abbiamo appena scriviamo la nuova classe DB, e il cambiamento (attraverso la fabbrica)

WorkerClass -> IDAL -> DALClass

a

WorkerClass -> IDAL -> MongoDBClass

interfacce descrivono le funzionalità. quando si scrive codice imperativo, parlare delle funzionalità che si sta utilizzando, invece di tipi o classi specifiche.

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