Moștenirea și polimorfismul - Ușurința de utilizare vs. Puritatea

Într-un proiect, echipa noastră utilizează liste de obiecte pentru a efectua operațiuni de masă pe seturi de date care ar trebui să fie toate procesate într-un mod similar. În special, obiecte diferite ar acționa în mod ideal la fel, ceea ce ar fi foarte ușor de atins cu polimorfismul. Problema pe care o am cu aceasta este că această moștenire implică relația este , mai degrabă decât are o relație . De exemplu, mai multe obiecte au un contor de daune , dar pentru a face acest lucru ușor de utilizat într-o listă de obiecte, ar putea fi folosit polimorfismul - cu excepția faptului că ar presupune o relație este nu ar fi adevărat. (O persoană nu este un contor de daune .)

Singura soluție la care mă pot gândi este ca un membru al clasei să returneze tipul obiectului propriu zis atunci când este implicit alcătuit în loc să se bazeze pe moștenire. Ar fi mai bine să renunțăm la ideea este / are un ideal în schimbul ușurării programării?

Editați | ×: Pentru a fi mai specific, folosesc C ++, deci utilizarea polimorfismului ar permite obiectelor diferite să "acționeze la fel" în sensul că clasele derivate ar putea locui într-o singură listă și să fie operate de o funcție virtuală a clasei de bază. Folosirea unei interfețe (sau imitarea acesteia prin moștenire) pare a fi o soluție pe care aș fi dispusă să o folosesc.

0
fr hi bn

10 răspunsuri

Cred că ar trebui să implementați interfețe pentru a putea impune relațiile dvs. are (fac acest lucru în C #):

public interface IDamageable
{
    void AddDamage(int i);
    int DamageCount {get;}
}

Ați putea implementa acest lucru în obiectele dvs.:

public class Person : IDamageable

public class House : IDamageable

Și ați fi sigur că proprietatea DamageCount și aveți o metodă care vă permite să adăugați daune, fără a sugera că o persoană și o casă sunt legate între ele într-un fel de heirarhie.

0
adăugat

În mod normal, atunci când vorbim despre "este un" vs "are un" vorbim despre moștenire vs compoziție.

Um ... contorul de daune ar fi doar atributul uneia dintre clasele tale derivate și nu ar fi cu adevărat discutat în termenii "O persoană este o contrare a prejudiciilor" cu privire la întrebarea ta.

Vezi asta:

http://www.artima.com/designtechniques/compoinh.html

Care ar putea să vă ajute pe parcurs.

@ Derek : Din formularea, am presupus că acolo era o clasă de bază, după ce am re-citit întrebarea pe care acum o văd acum la ce se întâmplă.

0
adăugat

Această întrebare este într-adevăr confuză: /

Întrebarea dvs. cu litere aldine este foarte deschis sa încheiat și are un răspuns de „depinde“, dar exemplul nu oferă într-adevăr multe informații despre contextul de la care se cere. Aceste linii mă confundă;

    

seturi de date care ar trebui să fie toate procesate în mod similar

  

În ce fel? Seturile sunt procesate de o funcție? O altă clasă? Prin intermediul unei funcții virtuale pe date?

    

În special, obiecte diferite ar acționa în mod ideal la fel, ceea ce ar fi foarte ușor de atins cu polimorfismul

  

Idealul de a "acționa la fel" și polimorfismul sunt absolut independente. Cum face polimorfismul să fie ușor de realizat?

0
adăugat

Uneori merită să renunțăm la idealul realist. Dacă va cauza o problemă masivă de a "face bine" fără nici un beneficiu real, aș face acest lucru greșit. Cu asta am spus că merită să ai timp să faci acest lucru corect, pentru că moștenirea multiplă inutilă crește complexitatea și poate contribui la menținerea mai puțin sustenabilă a sistemului. Chiar trebuie să decideți ce este mai bine pentru circumstanțele voastre.

O opțiune ar fi ca aceste obiecte să implementeze o interfață Damageable , mai degrabă decât să moștenească din DamageCounter . În acest fel, o persoană are un număr de pagube , dar este deteriorabilă. (Adesea găsesc că interfețele sunt mult mai înțelese ca adjective decât substantive.) Apoi, ați putea avea o interfață de deteriorare consistentă pe obiectele Damageable și nu expuneți faptul că un contor de daune este implementarea de bază (dacă nu aveți nevoie de la).

Dacă doriți să mergeți la ruta șablonului (presupunând C sau similar), ați putea face acest lucru cu mixuri, dar acest lucru poate deveni urât într-adevăr repede dacă este făcut prost.

0
adăugat

@Kevin

În mod normal, atunci când vorbim despre "este un" vs "are un" vorbim despre moștenire vs compoziție. "

     

Contorul de daune Um ... ar fi doar atributul uneia dintre clasele tale derivate și nu ar fi discutat cu adevărat în termenii "O persoană este o contrare a daunelor" cu privire la întrebarea ta.

Having the damage counter as an attribute doesn't allow him to diverse objects with damage counters into a collection. For example, a person and a car might both have damage counters, but you can't have a vector or a vector or anything similar in most languages. If you have a common Object base class, then you can shove them in that way, but then you can't access the getDamage() method generically.

Aceasta a fost esența întrebării sale, așa cum am citit-o. "Ar trebui să încalc codul și să-o pentru a trata anumite obiecte ca și cum ar fi aceleași, chiar dacă nu?

@Andrew

Idealul de a "acționa la fel" și polimorfismul sunt absolut independente. Cum face polimorfismul ușor de realizat?

Toți au, de exemplu, o funcție comună. Să numim addDamage() . Dacă doriți să faceți ceva de genul:

foreach (obj in mylist)
    obj.addDamage(1)

Apoi, aveți nevoie fie de o limbă dinamică, fie aveți nevoie ca ele să se extindă dintr-o clasă părinte comună (sau interfață). de exemplu.:

class Person : DamageCounter {}
class Car : DamageCounter {}

foreach (DamageCounter d in mylist)
    d.addDamage(1)

Apoi, puteți trata Person și Car același în anumite circumstanțe foarte utile.

0
adăugat

Polimorfismul nu necesită moștenire . Polimorfismul este ceea ce obții atunci când mai multe obiecte implementează aceeași semnătură de mesaj (metoda).

0
adăugat

Sunt de acord cu Jon, dar presupunând că totuși aveți nevoie de o clasă de contraincendiu, puteți face:

class IDamageable {
  virtual DamageCounter* damage_counter() = 0;
};
class DamageCounter {
  ...
};

Fiecare clasă daunatoare trebuie să furnizeze propria funcție de membru damage_counter (). Dezavantajul este că creează o vtable pentru fiecare clasă care poate fi deteriorată. Puteți folosi în schimb:

class Damageable {
 public:
  DamageCounter damage_counter() { return damage_counter_; }
 private:
  DamageCounter damage_counter_;
};

Dar mulți oameni sunt Nu răcești cu moștenire multiplă atunci când mai mulți părinți au variabile membre.

0
adăugat

"A face bine" va avea beneficii pe termen lung, chiar dacă pentru că cineva care întreține sistemul mai târziu va fi mai ușor să înțeleagă dacă s-ar fi făcut bine să începem.

În funcție de limbă, este posibil să aveți opțiunea de moștenire multiplă, dar în mod normal interfețele simple fac cel mai mult sens. Prin "simplu" vreau să fac o interfață care nu încearcă să fie prea mult. Mai bine să aveți o mulțime de interfețe simple și câteva monolitice. Desigur, există întotdeauna un compromis, și prea multe interfețe ar duce, probabil, la cei "uitați" despre ...

0
adăugat

Acest lucru poate fi realizat folosind mai multe moșteniri. În cazul dvs. specific (C ++), puteți utiliza clase virtuale pure ca interfețe. Acest lucru vă permite să aveți mai multe moșteniri fără a crea probleme de ambiguitate. Exemplu:

class Damage {
    virtual void addDamage(int d) = 0;
    virtual int getDamage() = 0;
};

class Person : public virtual Damage {
    void addDamage(int d) {
       //...
        damage += d * 2;
    }

    int getDamage() {
        return damage;
    }
};

class Car : public virtual Damage {
    void addDamage(int d) {
       //...
        damage += d;
    }

    int getDamage() {
        return damage;
    }
};

Acum, atât Persoana, cât și Mașina "sunt" Daune, adică pun în aplicare interfața Damage. Utilizarea de clase virtuale pure (astfel încât acestea sunt ca interfețe) este cheia și ar trebui să fie utilizate frecvent. Ea insulează viitoarele schimbări de la modificarea întregului sistem. Citiți mai întâi principiul Open-Closed pentru mai multe informații.

0
adăugat