2016-08-18 1 views
1

J'ai trouvé beaucoup de messages similaires sur des questions connexes, cette réponse par taylonr est venu près, mais n'a pas tout à fait répondre à mon scénario.Comment puis-je mieux concevoir cela en utilisant les principes OO et SOLID?

Supposons que j'ai une interface:

public interface IShape 
{ 
    decimal GetArea(); 
} 

je puis créer 3 classes en utilisant cette interface

public class Rectangle : IShape 
{ 
    public decimal GetArea() 
    { 
     ... 
    } 
} 

public class Triangle : IShape 
{ 
    public decimal GetArea() 
    { 
     ... 
    } 
} 

public class Circle : IShape 
{ 
    public decimal GetArea() 
    { 
     ... 
    } 
} 

Je veux maintenant ajouter une fonction int GetNumberOfSides() (ou une autre fonction qui ne concerne que certains de mes IShape). Évidemment, ceci n'est pas pertinent pour la classe Circle. Si j'avais alors un objet List<IShape> et que je souhaitais itérer pour appeler cette fonction sur des IShapes pertinentes, comment utiliseriez-vous les principes de conception orientés objet pour y remédier?

Je pourrais ajouter un booléen à mon interface IShape bool HasSides { get; set; } et exécuter n'importe quelle logique de ceci, cependant je devrais lancer à une classe spécifique afin d'accéder à une fonction GetNumberOfSides(). Je sais que ce n'est pas correct, car je ne me sens pas bien, mais je ne suis pas sûr de savoir comment faire.

J'ai pensé à avoir une autre interface ISidedShape qui a hérité de IShape, mais ensuite revenir à l'itération de ma liste comment saurais-je laquelle des formes avait cette méthode spécifique?

Toute aide serait grandement appréciée.

Merci

+2

Un 'Circle' ne peut-il simplement renvoyer" 0 "pour le nombre de côtés? Qu'en est-il d'autres formes étranges, comme un demi-cercle? Cela aurait légitimement 1 "côté", ainsi que 1 "autre chose qu'un côté, selon la façon dont vous définissez un côté". Peut-être que "côté" n'est pas le terme que vous recherchez? Avec un nom plus approprié pour le terme, la modélisation pourrait avoir plus de sens. – David

+0

Mon exemple n'a peut-être pas été le meilleur. J'avais du mal à trouver un exemple sans utiliser mon scénario réel qui nécessiterait d'autres connaissances. Je comprends votre point de vue sur le retour 0 mais cela ne me semble pas non plus. – user1244893

+2

Pourquoi ne vous sentez-vous pas bien? C'est parfaitement logique. – jrahhali

Répondre

1

Si vous voulez aller la route POO, vous pouvez ajouter une interface, puisque vous pouvez mettre en œuvre autant que vous le souhaitez dans la plupart des langues:

public interface IPolygon 
{ 
    int GetNumberOfSides(); 
} 

public class Rectangle : IShape, IPolygon 
{ 
    public decimal GetArea() { return 0; } 
    public int GetNumberOfSides() { return 4; } 
} 

Il est vrai que tous les polygones sont des formes; cependant, les dernières années ont enseigné à de nombreux OOPers que l'héritage peut être à la fois puissant et incroyablement dangereux, et qu'en général, une relation composite est préférée à une relation hiérarchique quand cela est logique. Je me sens obligé de dire que bien que ce soit un bon exercice académique, l'exemple de la POO par «formes» par excellence ne le traduit pas souvent dans le «monde réel» et en tant que tel, de nombreux programmeurs se compliquent trop. autrement, soyez une approche simple. Puisque tout doit être un objet, nous sommes contraints de cartographier nos données sur le modèle objet, ce qui signifie souvent que nous adaptons de force nos données au paradigme ou développons des constructions pour que les choses fonctionnent, au lieu de simplement travailler avec les données. "Simple" est roi, et la complexité est l'ennemi.

+0

Merci Josh. Lorsque vous bouclez sur une 'Liste ', utiliseriez-vous '' est IPolygon' pour déterminer si vous voulez le convertir en 'IPolygon'? – user1244893

0

Si vous êtes déjà avoir List<IShape> dans certains endroits dans votre code (basez-vous sur IShape polymorphisme), je dirais que la meilleure solution est d'ajouter GetNumberOfSides()-IShape et mettre en œuvre comme

public class Circle : IShape 
{ 
    public decimal GetNumberOfSides() 
    { 
     return 0; 
    } 
} 

Si vous êtes encore dans la phase de "conception" et vous n'avez pas de code qui utilise List<IShape> vous pourriez implémenter une deuxième interface IPolygon (exactement comme @Josh réponse).

L'une ou l'autre de ces deux solutions est adaptée (en fonction de la manière dont IShape est actuellement utilisé) et une "approche POO". Je ne peux pas dire lequel est le meilleur parce que vous ne nous avez pas montré votre vrai code (et il ne serait pas pertinent de prendre une telle décision basée sur un exemple de forme).

Cependant, la seule approche que je déconseille ajoute une méthode hasSides dans IShape parce que votre code sera bientôt encombré avec beaucoup de if à chaque fois que vous itérer sur un List<IShape> et sera donc un cauchemar pour tester et maintenir. Donc s'il vous plaît laissez le polymorphisme gérer la branche au lieu de ramifier manuellement avec if sur boolean.

+0

Ne seriez-vous pas en train de remplacer 'si hasSides' par' si xxx est IPolygon' lorsque vous bouclez sur votre 'Liste '? – user1244893

+0

@ user1244893 Si vous concevez votre système à l'avance avec 'IPolygon', une option acceptable (peut-être) serait d'avoir aussi une' List '. Cependant, vous auriez deux listes à maintenir maintenant au lieu d'une. Donc la première approche (ajouter 'GetNumberOfSides' à' IShape') me semble la plus facile à maintenir. – Spotted

0

Il relève du principe LSP et SI Si vous ajoutez la méthode getSides() à l'interface IShape, cela violera le LSP lorsque cette interface est implémentée par Circle car elle n'a pas de côtés. Vous voulez donc créer une nouvelle interface IPolygon comme mention dans l'un des messages pour qu'il suive à la fois l'Is et le LSP