Inheritance vs varying instances? - c#

If I were modelling various brands of cars would I use an inheritance hierarchy, or just varying constructor parameters?
What is the general rule for whether to relate objects using inheritance, or just by re-using the same class?
For cars I could just do something like new Car("Porsche","991","3.8") or I could have an overall abstract Car superclass, with abstract subclass manufacturers like "Porsche" and then possibly a class for each model of Porsche?

If you have a few properties that are shared by all cars (or methods that act on the object), and then unique properties (or methods) for each make/model, then you'd want to use inheritance. Otherwise, just varying instances are fine.
Let's just say you want these properties for all cars:
Make
Model
Year
Number of doors
In this case, you wouldn't want to create a class hierarchy, because it doesn't buy you anything.
Instead, if you had two "types" of cars: regular and race-car, and only the race-car could enable Nitrous Oxide (presumably a method that does this), you'd want a Car class, with RegularCar and RaceCar inheriting from it.
If you're just afraid of having to pass the same parameters to your constructor all the time, you can create static methods that call the constructor on your behalf. This is known as the Factory method pattern.
PS: my example really just came off the top of my head. But I hope you get the idea of what I'm trying to say :)

Creating subclasses is about managing complexity and splitting your problem into smaller and more simpler distinct and non overlapping cases. If you had to solve some problems for all the cars, and you think, this problem behave "specially" for Porsche because they have an extra backup engine that normal cars don't have, you could create something like
PorscheCar : Car
{
Engine engine;
Engine backupEngine;
}
NonPorscheCar : Car
{
Engine singleEngine;
}
So after you've decided to create a class that will solve a problem, if you detect multiple cases inside that problem, that you can, and decide to solve independently, you can create a subclass for each one of those. You can always solve every problem without using subclasses. The issue with that, is that I'll be a lot difficult to properly manage and organize the code if you don't create subclasses when you should create them.
You have to be careful to select what each problems each object or classes solve. If you want your Porsche to be printed like Porsche and normal cars without the *, it doesn't mean that Porsche "behaves" different, and you need to create a specific subclass. What actually behaves differently is how you print Porsche, so you should create something like:
Car
{
Brand brand;
}
CarPrinter
{
Car carToPrint;
static CreatePrinter(Car car)
{
return car.Brand() == PorscheBrand() ? new PorscheCarPrinter(car) : new DefaultCarPrinter(car);
}
}
PorscheCarPrinter
{
Print();
}
DefaultCarPrinter
{
Print();
}
So the problem that is splited into two cases is printing cars, not modeling cars per se.
In a more abstract way, you should create a class to model each concept of your domain. When you detect that the responsibility modeled and solved by a specific concept of that domain is complex, and you want to split it into smaller and distinct and non overlapping cases, then you should create a subclass for each of those smaller problems.
In your particular case, I don't know your entire problem, but it's highly unlikely you need to create subclasses for each brand of cars, since they are usually a single concept with just a single case to solve.

The problem with the single class approach is that you are stuck with the same instance variables and methods for all the different kinds of Cars.
For example, an Autobot might have a transform() method that most other cars would not have.
I mean, in theory you could define a transform() method for all Cars and have cars that don't support it throw an exception. But that's a much messier design than using inheritance.
Inheritance also gives you do fancy things like polymorphism. So for example, each car might have a parallelPark() method. But fancier cars might have an automated parallel parking mechanism that differs from all other cars. In that case, you simply override the parallelPark() method and Java will pick the right method.

I'd say that depends on how much logic is dependent on those properties like manufacturer and model. If it's none, it makes sense to do new Car("Porsche","991","3.8"), since the manufacturer and model are just properties. However, if the manufacturer and model define a single set of other properties, or of logic, then classes might make sense. That way instead of passing new Car("Porsche","991","3.8", gearingRatios, topSpeed, price, etc) every time you want a Porsche 991, you can just define a Porsche991 class that defines all of those properties.
Or, expanding more on the logic portion of it, if Porsche and Volkswagen cars would have very different logic in, say, the Accelerate() method, then instead of having one Accelerate() with a not-extensible switch/case method, you can use inheritance. Or if Porsches need a Race() method while Volkswagens do not, etc.
It's difficult to generalize this to a Car example without knowing how you plan on using the Car.

Take a look at the type object pattern (pdf).

For a superclass like cars, the more appropriate subclasses would be coupes, sedans, and convertibles. Car models should be a parameter.

Related

How to separate/decouple instance creation of children when creating core model in DDD

A course can have multiple activities, i.e. Training, Exam, Project, Book, Article, and Task.
Following are the requirements:
Allow the teacher to schedule a course.
Allow the teacher to schedule different activities in the said course.
Display list of activities to the student for a selected course, in a specified date range.
The above requirements lead me to create two aggregates.
CourseAggregate
ActivityAggregate
Why?
A course can be created without any activities, but only in draft state. A course can be scheduled on for a different set of students.
An activity can be created independent of course, and later on, linked to a course.
Activities can be fetched with a date range only for a given student.
protected abstract class Activity
{
public Guid Id {get; private set;}
}
protected class Training : Activity
{
..... Addiontal properties
}
protected class Exam : Activity
{
....Addiontal properties and behavior.
public bool AllowGrading => true;
}
.... Other childern of activity..hence more classes.
Questions:
Is it the right approach to go with inheritance?
Since I marked the constructor protected, so the client code will not use the new operator, and will not have direct knowledge of children. I am struggling to figure out how the client should create an instance of the activity. For example:
[Test]
public void ActivityFactoryShouldCreateCorrectActivityType(){
var activity= ActivityFactory.CreateActivity(activityType:"Training", title:"Training", DueDate: "date".......)
}
Problem is, each subtype might want to enforce different invariants for the entity to be correctly created. For example, Exam activity requires information about the scale of grading.
How to solve correctly implement it or which pattern suits better here?
That is one of the problem that frquently pops up when using a language like C# or Java. That is an implementational problem more than it is modeling issue.
The thing is that you do have these concepts: Exam, Training etc. that are concrete. On the other hand you can derive a common concept for them: Activity.
Here are couple of questions that we need to ask before we consider an implementation.
What it needs to do with these concepts?
How does it works with them?
How many parts of the system are interested in the concrete concepts Exam, Training etc. and how many of it is interested in the common concept of Activity?
Do you expect to add many more concepts that will be Activities? This will affect how you evolve your system.
Let's say that your system doesn't use the concept of Activity much and it wont have many more activities added. In this case we can just ignore Activity and just use concrete concepts. This say there is no problem in creating them.
Let's say that your system will use the concept of Activity and you need to add more types of activities.
This doesn't undermine the fact that your system will know of the different concrete types of activities. It will create them, work with them etc. Even when your system is working with the concept of activity it will probably still need to know the concrete type of the activity so it can do something with it.
This kind of logic shows a problem with the way that we think when we use an OOP language like C# of Java. We are trained as developers. usually people say that casting is bad. You sould somehow define a base class or an interface and let subclasses of interface implementers define a behavior and the other parts of the system shouldn't know the concrete type.
And this it true for some parts of the system and for come concepts. Take for example a Serializer. You can define an interface ISerializer with a method Serialize. The system that uses a serializer may use the interface without having to know the concrete type as each class that implements the ISerializer interface will add a different implementation of the same interface.
Not every problem is like that. Sometimes your system needs to know what it deals with. This is where i thing we can learn something from languages like JavaScript. There what you have is an object that is non specific and use can just attach properties to it. The object is what it's properties define it to be.
The concept of Duck Typing is interesting: "If it walks like a duck and it quacks like a duck, then it must be a duck"
If you system needs to work with Exam it should work with it not with an Activity. If it has an Activity it should be able to figure out it it's indeed an Exam because this is that it needs.
Now we live in the strong typed world and it has it's good parts. I love strong typing and what it gives you, but also some problems are more difficult to deal with.
You can use classes with inheritance to implement this. You also use interfaces instead of having classes to capture different concepts. Yet your system will need to do some casting to determine the concrete type of what is working with. We can make it's life a bit easier if we capture the fact that we have different types of Activities explicitly
Here's an example:
public enum ActivityType { Exam, Trainig, Project, Task }
public class Activity {
public Guid ID { get; private set; }
public abstract ActivityType Type { get; }
// other stuff
}
public class Exam : Activity {
public override ActivityType Type {
get { return ActivityType.Exam; }
}
// other stuff
}
public class SomeClass {
public void SomeAction(Activity activity) {
if(activity.Type == ActivityType.Exam) {
var examActivity = (Exam)activity;
// do something with examActivity
}
}
}
If creating your activities have some logic related to them you can use a Factory to create them by using their concrete types.
public class ExamFactory {
public Exam CreateSummerExam(string name, //other stuff) {
// Enfore constraints
return new Exam(new Guid(), name,....);
}
}
Or add a Factory to the concrete type:
public class Exam : Activity {
public static Exam CreateSummerExam() {
// Enfore constraints
return new Exam();
}
private Exam() { }
}
Or just use a public constructor if creating these objects is not complex.
If you realy want to hide the classes to allow yourself some freedom of implementation then use interfaces:
// **Domain.dll**
public enum ActivityType { Exam, Training }
public interface IActivity {
ActivityType Type { get; }
}
public interface IExam : IActivity { }
internal class Exam : IExam { }
public class ActivityFactory {
public IExam CreateExam() { return new Exam(); }
public ITraining CreateTraining() { return new Training(); }
// other concrete activities
}
This way you don't allow clien code to have access to classes. You can give them access to public interfaces and keep other implementation specific methods internal to your Domain.dll. Clients of these concepts can still use casting to use the appropriate type that they need, but this time they will use interfaces.
Here's a good article on this. In it Martin Fowler says:
With OO programs you try to avoid asking an object whether it is an
instance of a type. But sometimes that is legitamate information for a
client to use, perhaps for a GUI display. Remember that asking an
object if it is an instance of a type is something different than
asking it if it is an instance of a class, since the types
(interfaces) and the classes (implementations) can be different.
EDIT:
Another implementation of this is to treat an Activity as a container that you can attach different things to it. This will give you a more flexible system. Unfortunately this won't remove the need for switching and checking if various features are present in your entity. It's possible to some degree but depending on your concrete case you may need to process an Activity from some external component and will need to swith and check.
For example you may want to generate some report. You may need to get some activities, process them and then generate some report based on some data stored in them. This cannot happen with attaching components to one activity as this operation requires multiple activities not a single one.
There are a lot of systems that do this kind of thing. Here are some examples:
Computer Games use something that is called Entity Component System.These systems are data oriented where an Entity is comprised of different Components. Each system then checks to see if a Component is attached to an Entity. For example you have a Rendering system that renders your scene with all players and stuff. This system will check if an entity has attached 3D model component. If it has it will render it.
The same approach is used in Flow Based Programming. It is also data driven where you send Information Packets that are composed of different properties. These properties can be simple or complex. Then you have Processes that are connected and pass data between each other. Each Process will search for specific type of data in a IP to check if it's supported by it.
Unity also supports using Entity Component System. But it also supports another similar approach to having active components that contain behavior and logic instead of passive data that is processed from external systems.
Feature based programming. Uses the notion of features that you can add to an object. It's used in CAD/CAM system, banking systems and many more
It's a good approach to use when having dynamic data that needs to be processed. Unfortunately this won't remove the need to do some if/else and swich. As already mentioned, when you need to process collections of Activities you will need to do some checking.
Note that the systems above don't try to avoid this. On the contrary. They embrace this fact and use dynamic data. It's no different then having a type fo the activities and switching on it. It's just that their approach give a more flexible system at the expence of doing a lot of checks.
If you system doesn't require that kind of dynamic data you can just use concrete classes instead of data objects that can store unlimited number of things. This will simplify some parts of your application. If you do need to compose different objects then you can use one of the approaches above.
Thank for taking the time to answer the question in detail & with beautiful insights.
Consider we are developing https://coursera.org site. There are two major high-level goals which system has to achieve.
- Teacher/Course Creator should be able to create (schedule) a Course. From creator point of view, he/she want to add Exam, training or other activities to course. But he/she will refer to it as "I am scheduling an exam activity in this course for the following dates, with the following criteria of fail/pass" or "I am scheduling a training activity, in this course." Now, if we go with IActivity interface approach along with ActvityType Enum, all the client code, will be using switch or if/else to see what type of activity it is, and this will flow to top-level i.e. UI, or even controllers or consumer classes,
if(activity.type==exam){ ((Exam)IActivity).DoSomething();}
But this looks acceptable given there is no good alternative, but it really clutters your code.
- From a student perspective, he/she is only interested in the following
-- show me the list of all activities I have to perform, but tell me what type of activities they are
-- Once I attempt/do an activity, he/she expect different behavior as well, for example, Training does not have any grading attached to it, while exam does.
--- Exam is allowed to take only once.
--- Summary Exam grading is different than Full Exam.
--- Summary Exam Allow Late Submission while Exam does not have that feature at all.
Now again in order to call the correct behavior of IActivity, enum is helpful but it is cluttering the code base at all levels, where the decision has to be made. And IActivity does not know about the behavior Exam at all, and exam can be of multiple types, thus adding to the complexity so another enum to see what kind of exam it is since Summary Exam and Full Exam only differs in grading behavior, and everything else is same. Now with this, another switch statement or if/else on all consumer classes.
* Factories will help with this, but I am worried it will become too complex, having different methods in factories, since Exam can be in a valid state (draft) with a different combination of properties.
So the system is interested both in Activity and concrete types i.e. Exam, Training, etc but in different scope.
** Additional complexity, what if the teacher wants to create a new type of activity which is saying "Its Path activity, the exam is only available when the student takes this training." Now, the student is still interested to see a list of all activities, just want to know the type of it (as a label).
Lately, I have been thinking about composition instead of inheritance, where there is only one type, Activity, and it has a set of a feature collection. Each feature contains its own behavior in its own class. Have not done it before, not sure if this sort of approach exists or even beneficial.
Thanks again for the detailed answer again, would love to hear your thoughts.

Is there a pattern where you have a class whose job it is to instantiate common implementations of another class?

I want to call it a "Helper" but this seems way too general.
Let's say I have a class called WidgetCranker and WidgetCranker can be set up to crank out widgets of the type Square, Keyhole and GearShape. I can also specify which Color I want my widgets to be and which Label I want stamped on them.
Now, setting up each individual instance of WidgetCranker is fairly involved, the constructor just gives you an empty WidgetCranker and you have to manually set the type and colour of widgets you want to crank.
WidgetCranker keyholeWidget = new WidgetCranker();
keyholeWidget.Type = WidgetTypes.Keyhole;
keyholeWidget.Color = WidgetColors.Red;
keyholeWidget.Label = "ACME Industries Prototype 1";
But I have a class that requires a lot of WidgetCrankers that pretty much all look the same except for the label. I want to make my code more readable and less laborious to maintain, so I create a helper class that does all the lifting for me. So the above now becomes:
WidgetCranker keyholeWidget = WidgetCrankerHelper.RedKeyhole("ACME Industries Prototype 1");
My question is twofold:
Is this an actual design pattern and if so, what do we call it? I want to call it a factory, but it most definitely isn't a factory pattern. We're creating exactly the same kind of object in every case, just instantiating them differently. I know it's a type of "Helper", but I want to be more specific than that if I can. It's a helper that does a very specific thing.
Given that "Helper" is a very generic name, I feel that just naming the method by what it produces isn't enough. I should name it so that it's obvious what it does. So would MakeRedKeyhole be better or BuildRedKeyhole? I don't want to use GetRedKeyhole because that implies we're getting back a reference to an existing instance and not creating a brand new one.
I tend to stay away from the term "Helper" as all classes are supposed to be helpful, right? :)
I think that calling this either Factory or Builder would be acceptable. The point of abstract factory is to encapsulate the construction/instantiation of an object. It could return different types, but it doesn't need to. The type consuming the factory shouldn't care.
I tend to use the "Builder" name when it is doing any complex construction like this.

Decorator Pattern vs Inheritance with examples

I've been experimenting with the decorator pattern to extend functionality of code you do not want to touch for example and I see how to implement it however I am now unsure why you don't just inherit from the original class and extend that way.
I have read that the decorator pattern allows you to add functionality at runtime whereas inheritance means its there at compile time.
I don't understand this.
Could someone explain this, provide examples and explain when its better to use decorator vs inheritance.
Thanks
Suppose you create a View class that displays your items in a certain way.
Now you decide you also want a version of it which is scrollable, so you create a ScrollableView which inherits the View.
Later you decide you also want a version with a border so you now need to make a BorderedView and a BorderdScrollableView.
If on the other hand you could make a decorator for each added styling. You would have the following classes:
View
ScrollableDecorator
BorderedDecorator
When you want a bordered scroll view you do:
new BorderedDecorator(new ScrollableDecorator(new View())).
So you can configure any combination of this with just the 3 classes. And you can add or remove them at runtime (suppose you click a button that says add border, you now wrap your view with a BorderDecorator ... while whith inheritance you need to implemented this view class if you haven't already, or you need to create a new view instance and copy all relevant data from the first view to the second view which is not as easy to do as just adding or removing wrappers).
Imagine a game like Civilization, where each square on the map can have a variety of resources attached to it (like, say, various ores, or wood, or oil, etc.).
If you used straight inheritance, you'd need to create a class for each kind of square. It'd be unwieldy to have
public class OilSquare {}
public class OilAndGoldSquare {}
public class GoldAndSilverSquare {}
// etc.
The Decorator Pattern allows one to mix and match without needing to create a rigid hierarchy. So, you'd have instead:
public class Square {}
public class GoldDec {}
public class SilverDec {}
public class OilDec {}
// ...
var crazyMix = new GoldDec(new SilverDec(new OilDec(new Square())));
Put another way, Decorators allow for the creation of pipeline behavior, with each step in the pipeline being swappable with another step.
As others have already said Decorators are good for adding "options" to things... The benefits come in the way you can chain methods etc. through the decorators.
Imagine I buy a car with options for leather interior, metallic paint and awesome spoiler...
There are 8 different combinations of the three options but with decorators you only need three extra classes.
The interesting thing though is the way the decorator pattern works. As a brief example:
public class MetallicPaint : Car
{
private Car car;
public MetallicPaint(Car wrappedCar)
{
car = wrappedCar;
}
public decimal Cost()
{
return car.Cost() + 500;
}
public string Description()
{
return car.Description() + ", Metallic Paint";
}
public string Speed()
{
return car.Speed();
}
[... {pass through other methods and properties to the car object}]
}
This isn't a complete example but highlights how the decorator can interact with the object it is decorating. And of course because it implements car it can be used just like a car in every other way (and passes through anything the decorator doesn't effect to the inner car object).
Of course if you had multiple of these decorators with a car nested inside each would in turn add their cost, their part of the description and maybe the spoiler would alter the speed whereas the others didn't...
In essence it allows you to modify an object in a much more modular and less fundamental way than inheritance would. Decorators should always be used as if they were the base object (in this case Car) so they should never expose any new methods or properties, just slightly change the effect of existing ones.
Decorator pattern is better than inheritance if you have many features to be added and you also require to have combination of these features. Suppose your base class is A, and you want to extend(decorate) this base class with feature f1,f2,f3,f4 and some combination of them like (f1,f2) and (f1,f3) and .. ; so you would require to create 4!=4*3*2*1=24 class in your hierarchy (4 for each feature and the rest for their combination). While, Using decorative pattern, you would only need to create 4 classes!
for #Seyed Morteza Mousavi in #Razvi post:
You are right, we can add two properties Scrollable and Bordered to View class, then check if the property is set to true so run the desired behaviour. But this requires that we already be aware of the number of the feature we require(which is not the case in decorator pattern). otherwise, with every new feature (say f1) we want to add to our class, we need to alter our main class, or inherit the main class (you would say) and add the property. Taking latter approach, you would further need to alter the part of the code which handles feature combination (this is not good, since it is not obeying the rule of thumb of "loose coupling!")
hope this helps.

Passing arguments between classes - use public properties or pass a properties class as argument?

So let's assume I have a class named ABC that will have a list of Point objects.
I need to make some drawing logic with them. Each one of those Point objects will have a Draw() method that will be called by the ABC class.
The Draw() method code will need info from ABC class.
I can only see two ways to make them have this info:
Having Abc class make public some properties that would allow draw() to make its decisions.
Having Abc class pass to draw() a class full of properties.
The properties in both cases would be the same, my question is what is preferred in this case. Maybe the second approach is more flexible? Maybe not? I don't see here a clear winner, but that sure has more to do with my inexperience than any other thing.
If there are other good approaches, feel free to share them.
Here are both cases:
class Abc1 {
public property a;
public property b;
public property c;
...
public property z;
public void method1();
...
public void methodn();
}
and here is approach 2:
class Abc2 {
//here we make take down all properties
public void method1();
...
public void methodn();
}
class Abc2MethodArgs {
//and we put them here. this class will be passed as argument to
//Point's draw() method!
public property a;
public property b;
public property c;
...
public property z;
}
Also, if there are any "formal" names for these two approaches, I'd like to know them so I can better choose the tags/thread name, so it's more useful for searching purposes. That or feel free to edit them.
The best approach depends on the nature of the information ABC needs to provide to the Point instances, the nature of the relationship between these classes, and the "expected" future for them. In other words there are a lot of qualitative factors.
If you do go with passing the Point an ABC instance, don't - rather, work out an appropriate abstraction for whatever it is Point needs from ABC, and encapsulate that in an interface. In static terms this is similar to simply creating a new class to encapsulate the information, but dynamically quite different.
The reason you shouldn't simply pass an instance of ABC is that it creates a circular dependency. Without going into too much detail, this should generally be regarded as a Very Bad Thing and avoided unless absolutely necessary.
And, at a more abstract level, it will make more sense and enable logical changes later if you identify the reason for this apparent circular dependency and factor that out - ie, create an interface to represent this 'data source for Points' role which ABC must fulfil. This role is distinct from the 'container for Points' role and that should be reflected in your design.
You could also pass the parameters to the draw() method - again this may be good or bad depending on a heap of factors. It's certainly not a Very Bad Thing, as long as you've thought about the implications.
It will be more work to create and maintain a separate class to pass state between ABC and point, but it's worth doing if you want to decouple point from ABC.
The main question is, how much does decoupling them matter to you, if it matters at all? If it makes sense in your domain for point instances to know about abc instances, it probably isn't worth creating the parameter class and you should just go with option 1.
Go with approach #2, but without the object. Just pass the parameters to Draw directly.
Since the Point class and ABC appear to have to mediate between themselves as to what to draw, why not call the draw() method on the Point, passing the actual ABC object as an argument. The ABC object can provide accessor methods (don't expose those properties!) and the point class (or subclass implementations) can decide what to call back on ABC for.
You may want to consider reversing the dependencies. Instead of Points accessing properties from ABC, have ABC set properties on the points when (or just before) calling "draw()" on each of them. Something similar to the Flyweight pattern used when rendering cells in Swing's JTables (see javadoc). You may also consider decoupling Point (data model) from PointDrawer (reusable rendering code). That way your Points will not depend on all those properties, only your PointDrawers will.
And yes, it is OO programming even if you explicitly pass in all parameters to each Point at drawing time - that way, Points have no dependency at all on either ABC or on ABC's would-be "parameter-passing class".

When to Subclass instead of differentiating the behaviour

I'm having difficulties deciding when I should be subclassing instead of just adding an instance variable that represents different modes of the class and then let the methods of the class act according to the selected mode.
For example, say I've a base car class. In my program I'll deal with three different types of cars. Race cars, busses and family models. Each will have their own implementation of gears, how they turn and seat setup. Should I subclass my car into the three different models or should I create a type variable and make the gears, turning and seating generic so they would act different depending on which car type was selected?
In my current situation I'm working on a game, and I've come to realise that it's starting to get a bit messy, so I ask advice on possibly refactoring of my current code. Basically there are different maps, and each map can be one of three modes. Depending on which mode the map is defined as there will be different behaviour and the map will be built in a different way. In one mode I might have to give out rentals to players and spawn creatures on a timeout basis, wherein another the player is responsable for spawning the creatures and yet in another there might be some automated spawned creatures alongside with player spawned ones and players constructing buildings. So I'm wondering whether it would be best to have a base map class, and then subclass it into each of the different modes, or whether to continue down my current path of adding differentiated behaviour depending on what the map type variable is set to.
All credits to AtmaWeapon of http://www.xtremevbtalk.com answering in this thread (Archive Link)
Core to both situations is what I feel is the fundamental rule of object-oriented design: the Single Responsibility Principle. Two ways to express it are:
"A class should have one, and only one, reason to change."
"A class should have one, and only one, responsibility."
SRP is an ideal that can't always be met, and following this principle is hard. I tend to shoot for "A class should have as few responsibilities as possible." Our brains are very good at convincing us that a very complicated single class is less complicated than several very simple classes. I have started doing my best to write smaller classes lately, and I've experienced a significant decrease in the number of errors in my code. Give it a shot for a few projects before dismissing it.
I first propose that instead of starting the design by creating a map base class and three child classes, start with a design that separates the unique behaviors of each map into a secondary class that represents generic "map behavior". This post is concerned with proving this approach is superior. It is hard for me to be specific without a fairly intimate knowledge of your code, but I'll use a very simple notion of a map:
Public Class Map
Public ReadOnly Property MapType As MapType
Public Sub Load(mapType)
Public Sub Start()
End Class
MapType indicates which of the three map types the map represents. When you want to change the map type, you call Load() with the map type you want to use; this does whatever it needs to do to clear the current map state, reset the background, etc. After a map is loaded, Start() is called. If the map has any behaviors like "spawn monster x every y seconds", Start() is responsible for configuring those behaviors.
This is what you have now, and you are wise to think it's a bad idea. Since I mentioned SRP, let's count the responsibilities of Map.
It has to manage state information for all three map types. (3+ responsibilities*)
Load() has to understand how to clear the state for all three map types and how to set up the initial state for all three map types (6 responsibilities)
Start() has to know what to do for each map type. (3 responsibilities)
*Technically each variable is a responsibility but I have simplified it.
For the final total, what happens if you add a fourth map type? You have to add more state variables (1+ responsibilities), update Load() to be able to clear and initialize state (2 responsibilities), and update Start() to handle the new behavior (1 responsibility). So:
Number of Map responsibilities: 12+
Number of changes required for new map: 4+
There's other problems too. Odds are, several of the map types will have similar state information, so you'll share variables among the states. This makes it more likely that Load() will forget to set or clear a variable, since you might not remember that one map uses _foo for one purpose and another uses it for a different purpose entirely.
It's not easy to test this, either. Suppose you want to write a test for the scenario "When I create a 'spawn monsters' map, the map should spawn one new monster every five seconds." It's easy to discuss how you might test this: create the map, set its type, start it, wait a little bit longer than five seconds, and check the enemy count. However, our interface currently has no "enemy count" property. We could add it, but what if this is the only map that has an enemy count? If we add the property, we'll have a property that's invalid in 2/3 of the cases. It's also not very clear that we are testing the "spawn monsters" map without reading the test's code, since all tests will be testing the Map class.
You could certainly make Map an abstract base class, Start() MustOverride, and derive one new type for each type of map. Now, the responsibility of Load() is somewhere else, because an object can't replace itself with a different instance. You may as well make a factory class for this:
Class MapCreator
Public Function GetMap(mapType) As Map
End Class
Now our Map hierarchy might look something like this (only one derived map was defined for simplicity):
Public MustInherit Class Map
Public MustOverride Sub Start()
End Class
Public Class RentalMap
Inherits Map
Public Overrides Sub Start()
End Class
Load() isn't needed anymore for reasons already discussed. MapType is superfluous on a map because you can check the type of the object to see what it is (unless you have several types of RentalMap, then it becomes useful again.) Start() is overridden in each derived class, so you've moved the responsibilities of state management to individual classes. Let's do another SRP check:
Map base class
0 responsibilities
Map derived class
Must manage state (1)
Must perform some type-specific work (1)
Total: 2 responsibilities
Adding a new map
(Same as above) 2 responsibilities
Total number of per-class responsibilities: 2
Cost of adding a new map class: 2
This is much better. What about our test scenario? We're in better shape but still not quite right. We can get away with putting a "number of enemies" property on our derived class because each class is separate and we can cast to specific map types if we need specific information. Still, what if you have RentalMapSlow and RentalMapFast? You have to duplicate your tests for each of these classes, since each has different logic. So if you've got 4 tests and 12 different maps, you'll be writing and slightly tweaking 48 tests. How do we fix this?
What did we do when we made the derived classes? We identified the part of the class that was changing each time and pushed it down into sub-classes. What if, instead of subclasses, we created a separate MapBehavior class that we can swap in and out at will? Let's see what this might look like with one derived behavior:
Public Class Map
Public ReadOnly Property Behavior As MapBehavior
Public Sub SetBehavior(behavior)
Public Sub Start()
End Class
Public MustInherit Class MapBehavior
Public MustOverride Sub Start()
End Class
Public Class PlayerSpawnBehavior
Public Property EnemiesPerSpawn As Integer
Public Property MaximumNumberOfEnemies As Integer
Public ReadOnly Property NumberOfEnemies As Integer
Public Sub SpawnEnemy()
Public Sub Start()
End Class
Now using a map involves giving it a specific MapBehavior and calling Start(), which delegates to the behavior's Start(). All state information is in the behavior object, so the map doesn't really have to know anything about it. Still, what if you want a specific map type, it seems inconvenient to have to create a behavior then create a map, right? So you derive some classes:
Public Class PlayerSpawnMap
Public Sub New()
MyBase.New(New PlayerSpawnBehavior())
End Sub
End Class
That's it, one line of code for a new class. Want a hard player spawn map?
Public Class HardPlayerSpawnMap
Public Sub New()
' Base constructor must be first line so call a function that creates the behavior
MyBase.New(CreateBehavior())
End Sub
Private Function CreateBehavior() As MapBehavior
Dim myBehavior As New PlayerSpawnBehavior()
myBehavior.EnemiesPerSpawn = 10
myBehavior.MaximumNumberOfEnemies = 300
End Function
End Class
So, how is this different from having properties on derived classes? From a behavioral standpoint there's not much different. From a testing viewpoint, this is a major breakthrough. PlayerSpawnBehavior has its own set of tests. But since HardPlayerSpawnMap and PlayerSpawnMap both use PlayerSpawnBehavior, then if I've tested PlayerSpawnBehavior I don't have to write any behavior-related tests for a map that uses the behavior! Let's compare test scenarios.
In the "one class with a type parameter" case, if there are 3 difficulty levels for 3 behaviors, and each behavior has 10 tests, you'll be writing 90 tests (not including tests to see if going from each behavior to another works.) In the "derived classes" scenario, you'll have 9 classes that need 10 tests each: 90 tests. In the "behavior class" scenario, you'll write 10 tests for each behavior: 30 tests.
Here's the responsibility tally:
Map has 1 responsibility: keep track of a behavior.
Behavior has 2 responsibilities: maintain state and perform actions.
Total number of per-class responsibilities: 3
Cost of adding a new map class: 0 (reuse a behavior) or 2 (new behavior)
So, my opinion is that the "behavior class" scenario is no more difficult to write than the "derived classes" scenario, but it can significantly reduce the burden of testing. I've read about techniques like this and dismissed them as "too much trouble" for years and only recently realized their value. This is why I wrote nearly 10,000 characters to explain it and justify it.
You should subclass wherever your child type is some sort of specialization of the parent type. In other words, you should avoid inheritance if you just need functionality. As the Liskov Substitution Principle states: "if S is a subtype of T, then objects of type T in a program may be replaced with objects of type S without altering any of the desirable properties of that program"
In your case i would go with a hybrid approach (this might be called composition, i don't know), where your map mode variable is actually a separate object that stores all related data/behavior to the map's mode. This way you can have as many modes as you like without actually doing too much to the Map class.
gutofb7 nailed it on the head as to when you want to subclass something. Giving a more concrete example: In your Car class, would it matter anywhere in your program what type of car you were dealing with it? now if you subclassed Map, how much code would you have to write that deals with specific subclasses?
In the particular problem you talked about with the maps and spawning, I think this is a case where you want to favour composition over inheritance. When you think about it, they aren't exactly three different types of map. Instead, they are the same map with three different strategies for spawning. So if possible, you should make the spawning function a separate class and have an instance of a spawning class as a member of your map. If all the other differences in "modes" for your maps are similar in nature, you might not have to subclass the map at all, although subclassing the different components (i.e. have a spawn_strategy base class and subclass the three types of spawning from that), or at least giving them a common interface, will probably be necessary.
Given your comment that each type of map is meant to be conceptually different, then I would suggest subclassing, as that seems to fulfill Liskov's substitution principle. However, that is not to say you should give up on composition entirely. For those properties which every type of map has, but may have different behaviour/implementation, you should consider making your base class have them as components. That way you can still mix and match functionality if you need to, while using inheritance to maintain a separation of concerns.
I don't program in C#, but in Ruby on Rails, Xcode, and Mootools (javascript OOP framework) the same question could be asked.
I don't like having a method that will never be used when a certain, permanent, property is the wrong one. Like if it's a VW Bug, certain gears will never be turned. That's silly.
If I find some methods like that I try to abstract everything out that can be shared among all my different "cars" into a parent class, with methods and properties to be used by every kind of car, and then define the sub classes with their specific methods.

Categories