Question

Je viens de trouver cette citation dans un livre sur la POO que je lis,

  

Un enfant est seulement autorisé à augmenter   fonctionnalité et ajouter des fonctionnalités.   Un enfant n'est jamais autorisé à enlever   fonctionnalité. Si vous trouvez que   les enfants doivent supprimer des fonctionnalités,   c'est une indication que l'enfant   devrait apparaître devant le parent dans la   hiérarchie d'héritage!

Mais ma question est la suivante: n’est-ce pas ce qui est primordial?

Était-ce utile?

La solution

Vous pouvez supprimer des fonctionnalités avec une substitution. Mais normalement, vous l'utilisez pour changer le comportement. Laisser la classe se comporter comme il se doit.

Si le comportement est supprimé, c'est très souvent le signe d'une mauvaise conception de classe.

Autres conseils

L'enfant ne peut pas supprimer une fonctionnalité. Il peut la modifier, mais vous ne pouvez pas, par exemple, rendre une méthode publique privée.

L'intérêt de l'héritage est que vous pouvez gérer l'enfant comme s'il s'agissait du parent. Si vous avez une sous-classe 'Personne' et une sous-classe 'Employé', cela n'aurait aucun sens que la classe Employé n'ait pas de méthode respirer ().

Lors du remplacement d'une méthode, il est possible d'appeler l'implémentation parent à un moment donné pendant votre remplacement. Utilisez donc le remplacement pour ajouter une fonctionnalité à l'implémentation parent.

Non. En fait, vous augmenteriez la fonctionnalité (de manière négative)

Supposons que votre nouvelle fonctionnalité est "ne faites rien". mais la méthode, ce que les clients de votre code voient toujours la même interface

Vous ne pouvez pas avoir une sous-classe qui supprime une méthode de son parent.

C'est possible

class Parent {
    public void method_one(){ 
        print "Hello";
    }
}

class Child extends Parent {
     public void method_one(){
         // do nothing
     }
 }

Mais ce n'est pas:

class Parent {
    public void method_one(){ 
        print "Hello";
    }
}

class Child extends Parent {
     // Attempt remove the method visibility, thus remove funcionality 
     private void method_one(){ 
         // do nothing
     }
 }

Et c’est un peu pour cette raison que le remplacement (et en général tous les membres virtuels) est quelque chose qui doit être fait très soigneusement ... En fait, en règle générale, lors de la substitution, vous devez essayer de coder à la fois la classe de base et la classe dérivée, de sorte que l'implémentation de la classe dérivée appelle d'abord l'implémentation de base, puis exécute ses fonctionnalités supplémentaires ...

mais ce principe n'est pas appliqué dans les langages OO et est souvent violé ...

Exemple expliquant pourquoi c'est mauvais

Imaginez que vous avez CompanyA designs Type (classe) Téléphone

namespace CompanyA {
   class Phone {
       public void Dial() {
        // do work to dial the phone here
       }
   }
}

Aucune société iagine B ne définit un autre type BetterPhone, qui utilise le téléphone de la société A comme type de base ...

namespace CompanyB {
   class BetterPhone: CompanyA.Phone {
       public void Dial()  {
           Console.WriteLine("BetterPhoneDial");
           EstablishConenction();
           base.Dial();
       }
   }
}

Maintenant, CompanyA, dont la classe téléphonique est utilisée par d'autres sociétés (sociétés C, D, etc.) décide qu'établir une connexion est une chose utile à avoir dans la classe et modifie CompanyA.Phone, en ajoutant un EsatblishCOnnection () méthode aussi, peut-être avec une implémentation différente ... Jusqu’à ce que nous ayons le "nouveau" mot-clé, ce scénario aurait cassé la classe BetterPhone de CompanyB ... lors de la première tentative d'utilisation de la nouvelle classe de base.

Si votre enfant doit supprimer la fonctionnalité de parent, ce dernier doit être déclaré en tant qu'interface. Car Interface est un mécanisme qui définit les contrats à respecter par son implémenteur.

E.g.


public interface IContract
{
  void DoWork();
}

public class BaseContract: IContract
{
 public virtual void DoWork()
 {
  //perform operation A
 }
}

Maintenant, si vous souhaitez déclarer une nouvelle classe EnhancedContract, vous pouvez la dériver à partir de BaseContract ou IContract en fonction des besoins. Si vous souhaitez effectuer des opérations supplémentaires sur l'opération A de la base, vous pouvez en hériter de BaseContract, comme indiqué ci-dessous.


public class EnhancedContract: BaseContract
{
  public override void DoWork()
  {
   //perform operation B
   base.DoWork();
   //perform operation C
  }
}

Mais si vous n'êtes pas intéressé par l'opération A dans la méthode DoWork de EnhancedContract, héritez-la de IContract.

Cela garantit que EnhancedWork exécutera DoWork (), mais il n'est pas garanti qu'il effectue l'opération A dans celui-ci.


public class EnhancedWork:IContract
{
  public void DoWork()
  {
   //perform operation D
  }
}

Ceci est important pour la compréhension car cela empêchera l'utilisateur de faire un casting inférieur à celui-ci.


EnhancedContract e = new EnhancedContract();
BaseContract b = e;

Je pense que toutes ces opérations sont importantes pour la compréhension du Principe d'ouverture / fermeture , principe de substitution de Liskov .

La règle du pouce pour l'héritage est "Ajouter des fonctionnalités supplémentaires à celle existante".

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top