I have a choice.

Option 1:

public class Sample
{
    bool IsRelevant { get; set; }
}

Option 2:

public class Sample
{
}

public class RelevantSample : Sample
{
}

Is there a clear well-known rule how to make this decision?

My research: I have heard about "Replace Conditional with Polymorphism" refactoring before, but it usually deals with large switch statements:

https://sourcemaking.com/refactoring/replace-conditional-with-polymorphism

https://stackoverflow.com/questions/234458/do-polymorphism-or-conditionals-promote-better-design

https://stackoverflow.com/questions/4866873/replace-conditional-with-polymorphism-nice-in-theory-but-not-practical

There is a somewhat related question that describes a different situation (flag as a method argument rather than part of a domain entity): Is it wrong to use a boolean parameter to determine behavior?

有帮助吗?

解决方案

I think the language choice is irrelevant. What is important in making the decision is how the information is used.

If Samples behave differently depending on the value of isRelevent then it absolutely makes sense to break it up into three (not two) classes. The base Sample class, a RelevantSample class and a IrrelevantSample class. All sample objects would be instantiated from one of the two later classes (thus following the heuristic that base classes should be abstract.) Other objects can then inform sample objects of events without concerning themselves over whether the sample is relevant or not.

However, if it's more a matter of other objects behaving differently depending on the relevancy of the sample, then you would want to go with option 1 and make isRelevant a field that can be queried.

其他提示

You have two good answers already; a third reason to stick with the property is that the pattern you are describing is a "one shot" pattern. Everything goes pear shaped when you add a second Boolean. We begin with:

public class Sample
{
    public bool IsRelevant { get; protected set; }
}

and we refactor this into:

public abstract class Sample {}
public class RelevantSample : Sample {}
public class IrrelevantSample : Sample {}

And now we realize, oh, wait, samples can also be frobby or antifrobby:

public abstract class Sample 
{
    public bool IsFrobby { get; protected set; }
}
public class RelevantSample : Sample {}
public class IrrelevantSample : Sample {}

And now how do we move that into the type system?

public class RelevantFrobbySample : RelevantSample {}
public class RelevantAntifrobbySample : RelevantSample {}
public class IrrelevantFrobbySample : IrrelevantSample {}
public class IrrelevantAntifrobbySample : IrrelevantSample {}

And now I want to make a method that takes only frobby samples. How do I do it?

Single inheritance languages require you to choose your "inheritance pivot" extremely carefully because you only get one shot at it.

Not in C#.

Encoding implied behavior into types is evil in C# and similar languages, because the only way to get info out of the type is if x is T sort of checks (or trickery with dynamic, or reflection). So any change to it (adding a new variant, changing the behavior of a type) mean you get to go into all your consumers, violating the Open Closed Principle.

许可以下: CC-BY-SA归因
scroll top