Design orientato agli oggetti

23

Supponiamo di avere il seguente:

     +--------+     +------+
     | Animal |     | Food |
     +-+------+     +----+-+
       ^                 ^
       |                 |
       |                 |
  +------+              +-------+
  | Deer |              | Grass |
  +------+              +-------+

Deer eredita da Animal e Grass eredita da Food .

Fin qui tutto bene. Animal oggetti possono mangiare Food oggetti.

Ora lasciamolo mescolare un po '. Aggiungiamo un Lion che eredita da Animal .

     +--------+     +------+
     | Animal |     | Food |
     +-+-----++     +----+-+
       ^     ^           ^
       |     |           |
       |     |           |
  +------+ +------+     +-------+
  | Deer | | Lion |     | Grass |
  +------+ +------+     +-------+

Ora abbiamo un problema perché Lion può mangiare sia Deer che Grass , ma Deer non è Food è Animal .

Senza usare l'ereditarietà multipla e usando la progettazione orientata agli oggetti, come risolvi questo problema?

FYI: ho usato il link per creare i diagrammi ASCII.

    
posta Michael Irey 19.07.2013 - 22:04
fonte

12 risposte

37

IS A relationships = Eredità

Lion è un animale

HA UN rapporto = Composizione

L'auto ha una ruota

PU DO FARE relazioni = Interfacce

ICanEat

    
risposta data 20.07.2013 - 02:16
fonte
13

OO è solo una metafora che si modella dopo il mondo reale. Ma le metafore vanno solo così lontano.

Normalmente non esiste un modo giusto per modellare qualcosa in OO. C'è un modo giusto per farlo per un problema particolare in un particolare dominio e non dovresti aspettarti che funzioni bene se cambi il tuo problema, anche se il dominio gli oggetti sono gli stessi.

Penso che questo sia un malinteso comune nella maggior parte dei Comp. Ing. gli studenti hanno nei loro primi anni. OO non è una soluzione universale, solo uno strumento decente per alcuni tipi di problemi che possono modellare il tuo dominio abbastanza bene.

Non ho risposto alla domanda, proprio perché ci mancano informazioni sul dominio. Ma tenendo conto di quanto sopra, potresti essere in grado di progettare qualcosa che si adatti alle tue esigenze.

    
risposta data 19.07.2013 - 22:35
fonte
9

Vuoi abbattere ulteriormente gli animali nelle loro sottoclassi (o almeno per quanto riguarda il senso di ciò che stai facendo). Dato che stai lavorando con quelli che sembrano animali di base e due tipi di cibo (piante e carne), ha senso usare carnivori ed erbivori per definire ulteriormente un animale e mantenerli separati. Ecco cosa ho disegnato per te.

             +----------------+                   +--------------------+
             |    Animal      |                   |      Food          |
             |----------------|<--+Interfaces+--->|--------------------|
             |                |                   |                    |
             +----------------+                   +--------------------+
                +           +                       +                 +
                |           |    Abstract Classes   |                 |
                |           |        |          |   |                 |
                v           v        v          v   v                 v
   +-----------------+  +----------------+     +------------+      +------------+
   |   Herbivore     |  |  Carnivore     |     |   Plant    |      |   Meat     |
   |-----------------|  |----------------|     |------------|      |------------|
   |Eat(Plant p)     |  |Eat(Meat m)     |     |            |      |            |
   |                 |  |                |     |            |      |            |
   +-----------------+  +----------------+     +------------+      +------------+
            +                    +                    +                   +
            |                    |                    |                   |
            v                    v                    v                   v
   +-----------------+  +----------------+     +------------+      +------------+
   |  Deer           |  |   Lion         |     |  Grass     |      |  DeerMeat  |
   |-----------------|  |----------------|     |------------|      |------------|
   |DeerMeat Die()      |void Kill(Deer) |     |            |      |            |
   +-----------------+  +----------------+     +------------+      +------------+
                                 ^                    ^
                                 |                    |
                                 |                    |
                              Concrete Classes -------+

Come puoi vedere, entrambi espongono un metodo di mangiare, ma ciò che mangia cambia. Il Leone può ora uccidere un cervo, il daino può morire e restituire DeerMeat, e la domanda originale di OP su come consentire a un leone di mangiare un cervo, ma non l'erba riceve risposta senza un intero ecosistema.

Naturalmente, questo diventa interessante molto rapidamente perché un Cervo potrebbe essere considerato anche un tipo di carne, ma per mantenere le cose semplici, creerei un metodo chiamato kill () sotto cervo, che restituisce una carne di cervo, e metto che come una classe concreta che estende la carne.

    
risposta data 19.07.2013 - 22:13
fonte
6

Il mio design sarebbe così:

  1. Gli alimenti sono dichiarati come interfacce; c'è un'interfaccia IFood e due interfacce derivate da essa: IMeat e IVegetable
  2. Gli animali implementano IMeat e Vegetables implementano IVegetable
  3. Gli animali hanno due discendenti, carnivori ed erbivori
  4. I carnivori hanno il metodo Eat che riceve un'istanza di IMeat
  5. Gli erbivori hanno il metodo Eat che riceve un'istanza di IVegetable
  6. Il leone scende da Carnivore
  7. I cervi discendono da Erbivoro
  8. L'erba discende dalla verdura

Poiché gli animali implementano IMeat e il cervo è un animale (erbivoro), il leone, che è un animale (carnivoro) che può mangiare, anche IMeat può mangiare cervo.

Il cervo è un erbivoro, quindi può mangiare erba perché implementa IVegetable.

I carnivori non possono mangiare all'Egable e gli erbivori non possono mangiarlo.

    
risposta data 19.07.2013 - 22:52
fonte
5

I cibi che un animale può mangiare in realtà non formano una gerarchia, in questo caso la natura ha fallito in modo inusuale per conformarsi alla semplice modellazione orientata agli oggetti (notare che anche se lo facesse, l'animale dovrebbe ereditare dal cibo, dato che è cibo ).

La conoscenza di ciò che un animale può mangiare non può vivere interamente con nessuna delle classi, quindi avere semplicemente un riferimento ad alcuni membri della gerarchia alimentare non può essere sufficiente per dirti cosa cose che puoi mangiare.

È una relazione molte a molte. Ciò significa che ogni volta che aggiungi un animale, devi capire cosa può mangiare e ogni volta che aggiungi un alimento, devi capire cosa può mangiarlo. Se esiste un'ulteriore struttura da sfruttare dipende da quali animali e alimenti si sta modellando.

L'ereditarietà multipla in realtà non risolve questo molto bene. Hai bisogno di una sorta di collezione di cose che un animale può mangiare o di animali che possono mangiare un cibo.

    
risposta data 19.07.2013 - 23:03
fonte
1

Affronterò il problema da un lato diverso: l'OOP riguarda il comportamento. Nel tuo caso, Grass ha qualche comportamento per essere figlio di Food ? Quindi, nel tuo caso, non ci sarà la classe Grass , o almeno, non sarà ereditata da Food . Inoltre, se hai bisogno di imporre chi può mangiare cosa al momento della compilazione, è discutibile se hai bisogno di Animal di astrazione. Inoltre, non è raro vedere carnivori che mangiano erba , anche se non per il sostentamento.

Quindi progetterei questo come (non andando a disturbare con ASCI art):

IEdible con proprietà Type , che è enum di carne, pianta, carcassa, ecc. (questo non cambierà spesso e non ha alcun comportamento specifico, quindi non è necessario modellarlo come classe hiearchy).

Animal con metodi CanEat(IEdible food) e Eat(IEdible food) , che sono logici. Quindi, gli animali specifici possono controllare ogni volta che possono mangiare il cibo dato in determinate circostanze e poi mangiare quel cibo per ottenere sostentamento / fare qualcos'altro. Inoltre, modellerei le classi Carnivore, Erbivoro, Omnivore come Modello di strategia , anziché come parte della gerarchia animale.

    
risposta data 20.07.2013 - 10:32
fonte
1

TL; DR: progetta o modella con un contesto.

Penso che la tua domanda sia difficile perché manca del contesto del problema reale che stai cercando di risolvere. Hai alcuni modelli e alcune relazioni, ma manca la struttura in cui ha bisogno di lavorare. Senza contesto, la modellazione e le metafore non funzionano bene, lasciando la porta aperta a più interpretazioni.

Penso che sia più produttivo concentrarsi su come i dati verranno consumati. Una volta ottenuto il modello di utilizzo dei dati, è più facile tornare indietro a ciò che dovrebbero essere i modelli e le relazioni.

Ad esempio, requisiti più dettagliati richiederanno diverse relazioni tra gli oggetti:

  • supporto Animals eat ing non- Food come Gastroliths
  • supporto Chocolate come Poison per Dogs , ma non per Humans

Se iniziamo con l'esercizio di come modellare la relazione semplice presentata, l'interfaccia alimentare può essere la migliore; e se questa è la somma totale di come le relazioni nel sistema, allora la tua multa. Tuttavia, solo alcuni requisiti o relazioni aggiuntive possono influire notevolmente sui modelli e sulle relazioni che hanno funzionato nel caso più semplice.

    
risposta data 20.07.2013 - 22:45
fonte
1

Approccio di composizione per eccesso di eredità ECS:

An entity is a collection of components.
Systems process entities through their components.

Lion has claws and fangs as weapons.
Lion has meat as food.
Lion has a hunger for meat.
Lion has an affinity towards other lions.

Deer has antlers and hooves as weapons.
Deer has meat as food.
Deer has a hunger for plants.

Grass has plant as food.

Pseudocodice:

lion = new Entity("Lion")
lion.put(new Claws)
lion.put(new Fangs)
lion.put(new Meat)
lion.put(new MeatHunger)
lion.put(new Affinity("Lion"))

deer = new Entity("Deer")
deer.put(new Antlers)
deer.put(new Hooves)
deer.put(new PlantHunger)

grass = new Entity("Grass")
grass.put(new Plant)

Nature è un system che scorre attraverso queste entità, cercando i componenti che hanno attraverso una funzione di query generalizzata. Nature causerà alle entità affamate di carne di attaccare altre entità che hanno carne come cibo usando le loro armi, a meno che non abbiano un'affinità con quell'entità. Se l'attacco riesce, l'entità si nutrirà della sua vittima, a quel punto la vittima si trasformerà in un cadavere privato di carne. Nature causerà entità con la fame di piante da nutrire su entità che hanno pianta come cibo, a condizione che esistano.

Nature({lion, deer, grass})

Nature(entities)
{
    for each entity in entities:
    {
       if entity.has("MeatHunger"):
           attack_meat(entity, entities.with("Meat", exclude = entity))
       if entity.has("PlantHunger"):
           eat_plants(entity, entites.with("Plant", exclude = entity))
    }
}

Forse vogliamo estendere Grass per avere un bisogno di luce solare e acqua, e vogliamo introdurre la luce solare e l'acqua nel nostro mondo. Tuttavia Grass non può cercarli direttamente, poiché non ha mobility . Anche Animals potrebbe avere bisogno di acqua, ma può cercarlo attivamente poiché hanno mobility . È abbastanza facile continuare a estendere e modificare questo modello senza rotture a cascata dell'intero progetto, poiché aggiungiamo solo nuovi componenti ed estendiamo il comportamento dei nostri sistemi (o il numero di sistemi).

    
risposta data 08.01.2016 - 02:39
fonte
0

With out using multiple inheritance, and using object oriented design, how do you solve this problem?

Come molte cose, dipende .

Dipende da ciò che tu vedi "questo problema".

  • È un problema di implementazione generale , ad es. come "aggirare" il assenza di ereditarietà multipla nella piattaforma scelta?
  • È un problema di progettazione solo per questo caso specifico , ad es. come modellare il fatto che gli animali sono anche cibo?
  • È un problema filosofico con il modello di dominio , ad es. sono "cibo" e classificazioni valide, necessarie e sufficienti per gli animali previsto applicazione pratica?

Se stai chiedendo del problema generale di implementazione, la risposta dipenderà dalle capacità del tuo ambiente. Le interfacce IFood e IAnimal potrebbero funzionare, con una sottoclasse EdibleAnimal che implementa entrambe le interfacce. Se il tuo ambiente non supporta le interfacce, fai in modo che Animal erediti dal cibo.

Se stai chiedendo di questo specifico problema di progettazione, fai in modo che Animal erediti dal cibo. È la cosa più semplice che potrebbe funzionare.

Se stai chiedendo di questi concetti di design, la risposta dipende strongmente da cosa intendi fare con il modello. Se è per un videogioco cane mangia cane o anche un'applicazione per tenere traccia degli orari di alimentazione in uno zoo, potrebbe essere sufficiente per lavorare. Se è per un modello concettuale per modelli comportamentali animali, è probabilmente un po 'superficiale.

    
risposta data 19.07.2013 - 23:21
fonte
0

L'ereditarietà dovrebbe essere usata per qualcosa che è sempre qualcos'altro e non può cambiare. L'erba non è sempre cibo Ad esempio, io non mangio erba.

Grass svolge il ruolo di un prodotto alimentare per alcuni animali.

    
risposta data 28.07.2013 - 05:11
fonte
0

Hai appena trovato la limitazione di base di OO.

OO funziona bene con strutture gerarchiche. Ma una volta che ti allontani da rigide gerarchie l'astrazione non funziona così bene.

So tutto sulle composizioni di metamorfosi, ecc. che sono usate per aggirare questi limiti, ma sono maldestri e, cosa più importante, portano a codice oscuro e difficile da seguire.

Le basi di dati relazionali sono state inventate principalmente per allontanarsi dai limiti di rigide strutture gerarchiche.

Per prendere il tuo esempio, l'erba potrebbe anche essere un materiale da costruzione, una materia prima per la carta, un materiale per l'abbigliamento, un'erbaccia o un raccolto.

Un cervo potrebbe essere un animale domestico, un animale da allevamento, uno zoo o una specie protetta.

Un leone potrebbe anche essere un animale da zoo o una specie protetta.

La vita non è semplice.

    
risposta data 31.07.2013 - 03:38
fonte
0

With out using multiple inheritance, and using object oriented design, how do you solve this problem?

Quale problema? Che cosa fa questo sistema? Finché non rispondi a questo, non ho idea di quali classi possano essere richieste. Stai cercando di modellare un'ecologia, con carnivori, erbivori e piante, proiettando popolazioni di specie nel futuro? Stai cercando di far giocare al computer 20 domande?

È una perdita di tempo iniziare la progettazione prima che siano stati definiti eventuali casi d'uso. Ho visto questo portato a estremi ridicoli quando un team di circa dieci ha iniziato a produrre un modello OO di una compagnia aerea utilizzando software attraverso le immagini. Hanno lavorato per due anni per la modellazione senza alcun problema di business. Alla fine il cliente si è stancato di aspettare e ha chiesto al team di risolvere un problema reale. Tutta quella modellazione era completamente inutile.

    
risposta data 08.01.2016 - 04:00
fonte

Leggi altre domande sui tag