Base class should not use derivatives
The open/closed principle states: software entities should be open for extension, but closed for modification. http://en.wikipedia.org/wiki/Open/closed_principle
Creating or modifying a derived class shouldn't require modifying the base class. A base class using its derivatives likely will, making extension no longer simple: bad design.
No warning when the derived class used is nested private in the base class (an encapsulated implementation detail).
Creating or modifying a derived class shouldn't require modifying the base class. A base class using its derivatives likely will, making extension no longer simple: bad design.
No warning when the derived class used is nested private in the base class (an encapsulated implementation detail).
How to fix
Understand the need for using derivatives, imagine a new design, refactor. Typically, encapsulate base-class access to derived classes behind an abstract or virtual method. Conditions on typeof(DerivedClass) need urgent refactoring the same way. A base class creating derived instances calls for the factory method pattern http://en.wikipedia.org/wiki/Factory_method_pattern or the abstract factory pattern http://en.wikipedia.org/wiki/Abstract_factory_pattern
Estimated Debt: 3 minutes per derived class used + 3 minutes per derived class member used.
Estimated Debt: 3 minutes per derived class used + 3 minutes per derived class member used.
CQLinq source
This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.
// <Name>Base class should not use derivatives</Name>
// <Id>ND1201:BaseClassShouldNotUseDerivatives</Id>
warnif count > 0
from baseClass in JustMyCode.Types
where baseClass.IsClass && baseClass.NbChildren > 0 // <-- for optimization!
let derivedClassesUsed = baseClass.DerivedTypes.UsedBy(baseClass)
// Don't warn when a base class is using nested private derived class
.Where(derivedClass =>
!(derivedClass.IsNested &&
derivedClass.Visibility == Visibility.Private &&
derivedClass.ParentType == baseClass))
where derivedClassesUsed.Count() > 0
let derivedClassesMemberUsed = derivedClassesUsed.SelectMany(c => c.Members).UsedBy(baseClass)
orderby derivedClassesMemberUsed.Count() descending
select new {
baseClass,
derivedClassesUsed,
derivedClassesMemberUsed,
Debt = 3*(derivedClassesUsed.Count()+derivedClassesMemberUsed.Count()).ToMinutes().ToDebt(),
Severity = Severity.High
}
//<Expl>
// The *base {0}* should not rely on its derivative{1?:s}, specifically the *{1}*,
// because it should remain *open* for the creation of new derived
// classes without requiring modifications.
//</Expl>
//<Desc>
// The **open/closed principle** states: *software entities should be open
// for extension, but closed for modification*.
// http://en.wikipedia.org/wiki/Open/closed_principle
//
// Creating or modifying a derived class shouldn't require *modifying* the
// base class. A base class using its derivatives likely will, making
// extension no longer simple: bad design.
//
// No warning when the derived class used is nested private in the base class
// (an encapsulated implementation detail).
//</Desc>
//<HowToFix>
// Understand the need for using derivatives, imagine a new design, refactor.
// Typically, encapsulate base-class access to derived classes behind an
// abstract or virtual method. Conditions on *typeof(DerivedClass)* need
// *urgent refactoring* the same way. A base class creating derived instances
// calls for the *factory method pattern*
// http://en.wikipedia.org/wiki/Factory_method_pattern
// or the *abstract factory pattern*
// http://en.wikipedia.org/wiki/Abstract_factory_pattern
//
// Estimated Debt: 3 minutes per derived class used + 3 minutes per derived
// class member used.
//</HowToFix>
Open in the interactive Rules Explorer · All default rules
Object Oriented Design: all 14 items
- Avoid interfaces too big ND1200
- Base class should not use derivatives ND1201
- Class shouldn't be too deep in inheritance tree ND1202
- Class with no descendant should be sealed if possible ND1203
- Overrides of Method() should call base.Method() ND1204
- Do not hide base class methods ND1205
- A stateless class or structure might be turned into a static type ND1206
- Non-static classes should be instantiated or turned to static ND1207
- Methods should be declared static if possible ND1208
- Constructor should not call a virtual method ND1209
- Avoid the Singleton pattern ND1210
- Don't assign static fields from instance methods ND1211
- Avoid empty interfaces ND1212
- Avoid types initialization cycles ND1213