Come modellare le mie classi child senza violare i principi OO

1

Sto provando a modellare un sistema esterno in Java e ad eseguire alcuni problemi.
Ho una manciata di tipi correlati che ho mappato insieme attraverso classi astratte (e talvolta concrete se apprezzabili).

In alcuni casi alcune delle sottocategorie si sono rivelate solo implementando un metodo chiamante che ha appena selezionato un URL esterno da utilizzare, mentre lasciava che l'ABC costruisse il payload per esso.

Esempio di seguito in Python (perché non ho Eclipse installato su questa macchina):

from abc import ABC,abstractmethod
class Parent(ABC):
    @abstractmethod
    def callExternal(self):
        ''' Each concrete child knows which external URL it needs to call '''
        pass

    def doSomething(self):
        ''' Represents the entry point 
        if this was Java, this would be the public method
        the rest would probably be protected '''
        urlOutput = self.callExternal()
        return ' '.join(['I am',urlOutput])

    def buildPayload(self):
        ''' Payloads for children are exactly the same
        In reality we are using builders to set members
        but the structure of the payload between children
        is the same '''
        return 'some payload'

class ChildOne(Parent):
    def callExternal(self):
        payload = super(ChildOne,self).buildPayload()
        apiOutput = 'ChildOne'#hit the api specific to child 1 here
        self.childOneSpecificMember = apiOutput
        return ' '.join([payload,apiOutput])

class ChildTwo(Parent):
    def callExternal(self):
        payload = super(ChildTwo,self).buildPayload()
        apiOutput = 'ChildTwo'#hit the api specific to child 2 here
        return ' '.join([payload, apiOutput])

if __name__ == '__main__':
    print(ChildOne().doSomething())
    print(ChildTwo().doSomething())

Il consumatore dell'API sceglierà quale classe concreta ha bisogno (di solito attraverso DI) e quindi chiama il metodo pubblico doSomething per inviare i dati all'API esterna e ottenere l'output.
Stiamo anche utilizzando builder che costruiscono le istanze concrete, dal momento che una singola istanza concreta può modellare più tipi di oggetti logicamente diversi e creiamo solo una sottoclasse se c'è una vera differenza.

Sono preoccupato per aver riscontrato alcuni problemi di progettazione qui. Penso che abbiamo eliminato la sostituzione di Liskov nell'esempio precedente (anche se se l'albero di ereditarietà si abbassasse di un livello dovremmo preoccuparcene), ma la chiamata del figlio alla classe genitrice per generare un carico utile è preoccupante così come il bambino che essenzialmente sta configurando il genitore chiamando un URL specifico.

    
posta Chris 29.05.2018 - 02:27
fonte

2 risposte

2

Qui il problema è che la creazione del "payload" è la stessa per ogni caso d'uso, ma l'interazione con l'API esterna è ciò che cambia.

Incapsula la cosa che cambia.

Non hai bisogno di sottoclassi, anche se non hai bisogno di meno classi. La composizione è tua amica qui. Quelle che chiamate sottoclassi dovrebbero essere tutte classi concrete senza un genitore esplicito, ognuna dovrebbe aderire alla stessa interfaccia pubblica. Quindi la classe Parent ha bisogno solo della classe API concreta come argomento del costruttore (prima di scrivere Python, quindi potrebbe essere un po 'confusa):

class Parent:
    apiClient = None

    def __init__(self, apiClient)
        self.apiClient = apiClient

    def doSomething(self):
        ''' Represents the entry point 
        if this was Java, this would be the public method
        the rest would probably be protected '''
        urlOutput = self.apiClient.call(self.buildPayload())
        return ' '.join(['I am',urlOutput])

    def buildPayload(self):
        ''' Payloads for children are exactly the same
        In reality we are using builders to set members
        but the structure of the payload between children
        is the same '''
        return 'some payload'

class ApiClientOne:
    def call(self, payload):
        apiOutput = 'ChildOne'#hit the api specific to child 1 here
        self.childOneSpecificMember = apiOutput
        return ' '.join([payload,apiOutput])

class ApiClientTwo:
    def call(self, payload):
        apiOutput = 'ChildTwo'#hit the api specific to child 2 here
        self.childOneSpecificMember = apiOutput
        return ' '.join([payload,apiOutput])
    
risposta data 29.05.2018 - 15:50
fonte
0

Non fornisci informazioni sufficienti per determinare se stai violando qualcosa. Il principio di sostituzione di Liskov non indirizza la costruzione di oggetti, solo la possibilità di sostituirne uno con un altro. Inoltre, anche una classe base abstract non è indirizzata, poiché non può esserci istanza della classe base che deve mantenere il contratto.

Se hai una fabbrica che costruisce la classe giusta nel modo giusto, c'è poca indicazione che stai violando qualcosa.

    
risposta data 29.05.2018 - 15:43
fonte

Leggi altre domande sui tag