È considerata una buona pratica dichiarare le interfacce di base senza mutatori, fornendo i mutatatori in una sotto-interfaccia

4

I ha fatto una domanda su StackOverflow su come implementare correttamente un ImmutableMap che rispetti i principi SOLID.

A causa dell'interfaccia Map di Java contenente put e putAll , viola il principio di segregazione dell'interfaccia forzandoci ad implementare i metodi put e putAll per ImmutableMap . Se dovessimo definire la nostra interfaccia, ricaderebbe nella categoria olfattiva del codice "Classi alternative con diverse interfacce".

Questo mi ha fatto pensare a come dichiarare correttamente un'interfaccia che si attenesse al principio di Subsitution di Liskov, pur rispettando il principio di segregazione dell'interfaccia.

L'unico modo in cui posso pensare di ottenere questo sarebbe dichiarare l'interfaccia di base con solo gli accessor, quindi fornire una subinterface con i mutatori:

interface Map {
    //accessor methods
}

interface MutableMap extends Map {
    //mutator methods
}

class HashMap implements MutableMap {

}

class ImmutableMap implements Map {

}

Questa è considerata una "buona pratica"? Sembra essere l'unico modo per implementare l'immutabilità pur rimanendo fedele ai principi SOLID, ma non ho mai sentito parlare di una simile pratica.

    
posta Vince Emigh 26.04.2015 - 03:31
fonte

2 risposte

7

Il design dell'interfaccia originale nella domanda suggeriva che l'interfaccia di Map richiedeva l'immutabilità, ma questo viola il principio di sostituzione di Liskov. Considera la sua definizione:

Let Φ(x) be a property provable about objects x of type T. Then Φ(y) should be true for objects y of type S where S is a subtype of T.

Considera l'immutabilità come la proprietà dimostrabile della mappa di interfaccia immutabile. Questa proprietà (immutabilità) non è chiaramente dimostrabile nell'interfaccia MutableMap.

Il design dell'interfaccia nella domanda è stato chiarito e ora non viola quello di Liskov. L'assenza dei soli metodi mutati non impone l'immutabilità.

Questo design più esplicito non viola il principio di sostituzione di Liskov:

/** Contract says nothing about (im)mutability, only that it provides read access */
interface Map {
    // accessor methods
}

/** This specialization explicitly allows mutation */
interface MutableMap extends Map {
    // mutator methods
}

/** This specialization adds an immutability constraint */
interface ImmutableMap extends Map {
    // could contain nothing (just marker interface)
}

La chiave qui è che l'interfaccia immutabile non è un antenato dell'interfaccia mutevole, sono entrambi "fratelli" che chiariscono (specializzano) i vincoli / le caratteristiche del comune antenato generico.

L'interfaccia della mappa non dice nulla sulla (im) mutabilità, ecco perché è possibile specificarlo nei suoi discendenti senza violare il principio di sostituzione di Liskov.

    
risposta data 26.04.2015 - 09:33
fonte
0

No, questa è una violazione del Principio di sostituzione di Liskov poiché stai espandendo il contratto implicito (da immutabile a mutabile). Puoi avere un Readable foo come classe base, ma devi prestare particolare attenzione affinché il codice non implichi l'immutabilità.

    
risposta data 26.04.2015 - 05:43
fonte