Related
I have a class Game and now I need to add players (IPlayer) to the game. And I'm thinking about two scenarios:
First approach
IPlayer AddPlayer()
In this scenario the caller does not have to worry about implementation of IPlayer. He get just an interface and cannot tamper with the object. The Game knows implementation details and can manage player's state, for example add points:
interface IPlayer
{
int Points { get; }
}
class Player : IPlayer
{
...
void AddPoints(int points);
}
Second approach
void AddPlayer(IPlayer player)
In this approach it's the caller who is responsible for delivering an object that meets the interface. Thus know sthe details.
I'm wondering: Is any of those approaches apparently better/worse?
Just a comment about my point of view:
My goal is to have clear API. And I think that the first approach is better. When I give a user my library then I give him just interface. All the details are maintained inside. (Similarly I could give an access to IGame that under the hood is an instance of Game)
I would strongly recommend using a "composition" approach as Unity does. Don't create classes/interfaces modeled around specific game entities. Have a GameObject class instead, exposing a collection of Components.
Each Component would add some features and behavior to the owning GameObject, like position, physics, stats and so on.
One GameObject instance can act as some sort of "game controller", spawning entities and keeping track of the score (or whatever rules your game has).
I want to implement a library for card games of all types. I found out that OOP would probably the best way to implement such a library. So I started with modelling of some classes for UI (which has concrete subclasses ConsoleUI, SDLUI, HTMLUI), Party (with the subclasses Player and Group), Card, CardAttribute, Turn, TurnCommand, CompoundTurnCommand. Those classes are as good as complete.
But now I detected that there are several different kinds of collections of cards. I thought to create an abstract class CardCollections with many subclasses:
CardCollection = abstract collection of CardCollections
Card = a single card
Hand = an indexed random access list. If you execute draw(index), all CardCollections after index are shifted left (like a linked list)
Tablet = an indexed random access list. If you execute draw(index), the CardCollections after index are not shifted, can contain gaps between cards (like an array/dictionary)
Pile = an LIFO collection (like a stack)
Stock = a FIFO collection (like a queue)
Row = a double-ended collection (like a deque)
Random = a non-indexed undordered collection (like a bag)
Because I want to follow common OOP design practises, like inheritance and polymorphism, I am looking for a proper model - and here the problem begins.
I started to create interfaces for all concrete classes:
ICard extends IHand and ITablet.
IHand extends IRandom and IRow.
ITablet extends ICardCollection.
IPile extends ICardCollection.
IStock extends ICardCollection.
IRow extends IPile and IStock.
IRandom extends ICardCollection.
But I am very unsure about the design because it requires many, many interfaces.
Is there a better and more proper way for this? Because the library should be independent of the programming language I don't expect concrete implementations.
This is certainly not an acceptable answer, and maybe even subjective, but ... too long for a comment, and maybe it encourages others to give more profound answers:
It's difficult to give design hints as long as the description is so vague in terms of programming language and intended usage. E.g. I'm not entirely sure whether you mentioned Card as one of several CardCollections - intuitively, a single Card is no collection of cards. Additonally, in your description, you talked about CardCollections where I had expected only a Card (e.g. "...all CardCollections after index are shifted..." - shouldn't this be "...all Cards after index are shifted..." ?)
However, I think the decision about the precise inheritance relationship mainly depends on the methods that these classes will contain, and how they are intended to be used. Given the limited information, my "generic" approach would roughly be the following:
Insert all methods that you'd expect in the respective class. A first guess:
CardCollection {
int size();
}
Hand {
Card draw(int index);
}
Tablet {
Card draw(int index);
}
Pile {
void putFront(Card card);
Card takeFront();
}
Stock {
void putEnd(Card card);
Card takeEnd();
}
Row {
void putFront(Card card);
Card takeFront();
void putEnd(Card card);
Card takeEnd();
}
Random {
???
}
Factor out common parts. This might be tricky. It already depends on whether the language of choice supports multiple inheritance or things like interfaces in Java. More importantly, it depends on whether the language supports non-public inheritance. For example, when you write class ExtendedClass extends BaseClass or class ExtendedClass implements BaseInterface in Java, then this is set in stone. Everybody who uses the API will rely on this inheritance, and it can never be changed after the first release. In contrast to C++, where you can write class ExtendedClass : private BaseClass and the user of the API will never know that you inherited from BaseClass. (I'm not familiar with Tcl, Smalltalk or Lua, so I can't say anything about that - and for sure, C++ is the best example for a not-so-easy-to-learn language ;-)).
Construct an inheritance hierarchy based on the common parts. This could for example be according to what you already described, e.g. Row extends Pile and Stock. Alternatively, you could classify the classes according to what they allow you to do, possibly in some abstract form that is possibly not even directly visible to the user:
// Private part:
FrontQueue {
void putFront(Card card);
Card takeFront();
}
EndQueue {
void putEnd(Card card);
Card takeEnd();
}
// Public part:
Pile extends FrontQueue {}
Stock extends EndQueue {}
Row extends FrontQueue, EndQueue {}
Of course, this once more depends on the language and the intended usage. One would also have to ask whether these classes should rather serve as a "facade", and the actual behavior should be implemented via composition instead of inheritance. An obvious example for a further questions is: Should every method that expects a Pile also accept a Row? Alternatives for the class design might also be considered. For example, instead of letting Row inherit from FrontQueue and EndQueue, one could declare
Row {
FrontQueue getFront();
EndQueue getEnd();
}
Again, these are just thoughts, and in the worst case, not answering your question, but instead raising new questions, but ... maybe the ones that you have to answer on the way to your goal anyhow.
your design has already been done a few times. except for card, all of your interfaces are very similar to collection, list, queue, stack, and deque. there are the java collections framework, the c# collections framework, and dart. you will probably come up with something similar.
i would suggest a spike using concrete implementations where everything extends collection, list, queue, stack, or deque then refactor out whatever interface hierarchy that you can find.
edit: i just noticed that you have: ICard extends IHand and ITablet. card is probably it's own interface. something like:
enum Type {poker, tarot, magic ... }
interface {
Type type();
Map<String,Object> attributeNameToAttributeValue();
}
you could have sub interfaces for card flavours like:
enum Suit { clubs, diamonds, hearts, spades }
enum Rank { deuce,trey,four,five,six,seven,eight,nine,ten,jack,queen,king,ace }
interface PokerCard extends Card {
Rank rank();
Suite suit();
}
The Liskov Substitution Principle states that a subtype should be substitutable for that type (without altering the correctness of the program).
Can someone please provide an example of this principle in the domain of vehicles (automotives)?
Can someone please provide an example of a violation of this principle in the domain of vehicles?
I've read about the square/rectangle example, but I think that an example with vehicles will give me a better understanding of the concept.
For me, this 1996 Quote from Uncle Bob (Robert C Martin) summarises the LSP best:
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
In recent times, as an alternative to inheritance abstractions based on sub-classing from a (usually abstract) base/super class, we also often use interfaces for polymorphic abstraction. The LSP has implications to both the consumer, and implementation of the abstraction:
Any code consuming a class or interface abstraction must assume nothing else about the class beyond the defined abstraction;
Any subclassing of a superclass or implementation of an abstraction must adhere to the requirements and conventions of the interface to the abstraction.
Uncle Bob, and others regard the LSP as a cornerstone of Design by Contract.
Example, LSP Compliance
Here is an example using an interface IVehicle which can have multiple implementations (alternatively, you can substitute the interface for an abstract base class with several subclasses - same effect).
interface IVehicle
{
void Drive(int miles);
void FillUpWithFuel();
int FuelRemaining {get; } // C# syntax for a readable property
}
This implementation of a consumer of IVehicle stays within the bounds of LSP:
void MethodWhichUsesIVehicle(IVehicle aVehicle)
{
...
// Knows only about the interface. Any IVehicle is supported
aVehicle.Drive(50);
}
Glaring Violation - Runtime type switching
Here's an example of a violation of LSP, using RTTI and then Downcasting - Uncle Bob calls this a 'glaring violation':
void MethodWhichViolatesLSP(IVehicle aVehicle)
{
if (aVehicle is Car)
{
var car = aVehicle as Car;
// Do something special for car - this method is not on the IVehicle interface
car.ChangeGear();
}
// etc.
}
The violating method goes beyond the contracted IVehicle interface and hacks a specific path for a known implementation of the interface (or a subclass, if using inheritance instead of interfaces). Uncle Bob also explains that LSP violations using type-switching behaviour usually also violate the Open and Closed principle as well, since continual modification to the function will be required in order to accomodate new subclasses.
Violation - Pre condition is strengthened by a subtype
Another violation example would be where a "pre condition is strengthened by a subtype":
public abstract class Vehicle
{
public virtual void Drive(int miles)
{
Assert(miles > 0 && miles < 300); // Consumers see this as the contract
}
}
public class Scooter : Vehicle
{
public override void Drive(int miles)
{
Assert(miles > 0 && miles < 50); // ** Violation
base.Drive(miles);
}
}
Here, the Scooter subclass attempts to Violate the LSP as it tries to strengthen (further constrain) the precondition on the base class Drive method that miles < 300, to now a maximum of less than 50 miles. This is invalid, since by the contract definition of Vehicle allows 300 miles.
Similarly, Post Conditions may not be weakened (i.e. relaxed) by a subtype.
(Users of Code Contracts in C# will note that preconditions and postconditions MUST be placed on the interface via a ContractClassFor class, and cannot be placed within implementation classes, thus avoiding the violation)
Subtle Violation - Abuse of an interface implementation by a subclass
A more subtle violation (also Uncle Bob's terminology) can be shown with a dubious derived class which implements the interface:
class ToyCar : IVehicle
{
public void Drive(int miles) { /* Show flashy lights, make random sounds */ }
public void FillUpWithFuel() {/* Again, more silly lights and noises*/}
public int FuelRemaining {get {return 0;}}
}
Here, irrespective of how far the ToyCar is driven, the fuel remaining will always be zero, which will be surprising to users of the IVehicle interface (i.e. infinite MPG consumption - perpetual motion?). In this case, the problem is that despite ToyCar having implemented all of the requirements of the interface, ToyCar just inherently isn't a real IVehicle and just "rubber stamps" the interface.
One way to to prevent your interfaces or abstract base classes from being abused in this way is to ensure a good set of Unit Tests are made available on the interface / abstract base class to test that all implementations meet the expectations (and any assumptions). Unit tests are also great at documenting typical usage. e.g. this NUnit Theory will reject ToyCar from making it into your production code base:
[Theory]
void EnsureThatIVehicleConsumesFuelWhenDriven(IVehicle vehicle)
{
vehicle.FillUpWithFuel();
Assert.IsTrue(vehicle.FuelRemaining > 0);
int fuelBeforeDrive = vehicle.FuelRemaining;
vehicle.Drive(20); // Fuel consumption is expected.
Assert.IsTrue(vehicle.FuelRemaining < fuelBeforeDrive);
}
Edit, Re: OpenDoor
Opening doors sounds like a different concern entirely, so needs to be separated accordingly (i.e. the "S" and "I" in SOLID), e.g.
Add a separate interface IDoor, and then vehicles like Car and Truck would implement both IVehicle and IDoor interfaces, but Scooter and Motorcycle would only implement IVehicle.
In all cases, to avoid violating LSP, code which required objects of these interfaces should not downcast the interface to access extra functionality. The code should select the appropriate minimum interface / (super)class it needs, and stick to just the contracted functionality on that interface.
Image I want to rent a car when I'm moving house. I ring up the hire company and ask them what models they have. They tell me though that I'll just be given the next car that comes available:
public class CarHireService {
public Car hireCar() {
return availableCarPool.getNextCar();
}
}
But they have given me a brochure that tells me all of their models come with these features:
public interface Car {
public void drive();
public void playRadio();
public void addLuggage();
}
That sounds just what I'm looking for, so I book a car & go away happy. On moving day, a Formula One car shows up outside my house:
public class FormulaOneCar implements Car {
public void drive() {
//Code to make it go super fast
}
public void addLuggage() {
throw new NotSupportedException("No room to carry luggage, sorry.");
}
public void playRadio() {
throw new NotSupportedException("Too heavy, none included.");
}
}
I'm not happy, because I was essentially lied to by their brochure — it doesn't matter if the Formula One car has a fake boot that looks like it can hold luggage but won't open, that's useless for moving house!
If I'm told that "these are the things all of our cars do", then any car I'm given should behave in this way. If I can't trust the details in their brochure, it's useless. That's the essence of the Liskov Substitution Principle.
The Liskov Substitution Principle states that an object with a certain interface can be replaced by a different object that implements that same interface while retaining all the correctness of the original program. That means that not only does the interface have to have exactly the same types, but the behavior has to remain correct as well.
In a vehicle, you should be able to replace a part with a different part, and the car would keep working. Let's say your old radio doesn't have a digital tuner, but you want to listen to HD radio so you buy a new radio that has an HD receiver. You should be able to take the old radio out and plug in the new radio, as long as it has the same interface. On the surface, that means the electrical plug that connects the radio to the car needs to be the same shape on the new radio as it is on the old radio. If the car's plug is rectangular and has 15 pins, then the new radio's jack needs to be rectangular and have 15 pins as well.
But there are other considerations besides the mechanical fit: the electrical behavior on the plug has to be the same, too. If pin 1 on the connector for the old radio is +12V, then pin 1 on the connector for the new radio also has to be +12V. If pin 1 on the new radio was the "left speaker out" pin, the radio might short out, or blow a fuse. That would be a clear violation of the LSP.
You could also consider a situation of a downgrade: let's say your expensive radio dies, and you can only afford an AM radio. It doesn't have stereo out, but it has the same connector as your existing radio. Let's say the spec has pin 3 being left speaker out, and pin 4 being right speaker out. If your AM radio plays the monophonic signal out both pins 3 and 4, you could say that its behavior is consistent, and that would be an acceptable substitution. But if your new AM radio plays audio only on pin 3, and nothing on pin 4, the sound would be unbalanced, and that probably would not be an acceptable substitution. That situation would also violate the LSP, because while you can hear sounds, and no fuses blow, the radio doesn't meet the full specification of the interface.
First, you need to define what a vehicle and automobile are. According to Google (not very complete definitions):
Vehicle:
a thing used for transporting people or goods, esp. on land, such as a car, truck, or cart.
Automobile:
a road vehicle, typically with four wheels, powered by an internal combustion engine or electric motor and able to carry a small number of people
So an automobile is a vehicle, but a vehicle is not an automobile.
In my opinion, to archieve the LSP, subtypes can never add new public methods. Just private methods and fields. And of course subtypes can override methods of the baseclass. If a subtype has a single public method which the basetype doesn't have, you simply cannot substitute the subtype with the basetype. If you pass an instance to a client's method whereby you receive a subtype's instance but the parameter's type is the basetype or if you have a collection of type basetype where also subytpes are part of, then how can you ever call the method of the subtype class without asking for it's type using an if statment and if the type matches, do a cast to that subtype in order to call the method on it.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
When to use an interface instead of an abstract class and vice versa?
Hi, I am teaching OOP concepts to non-programmers. I wanted to know how can you explain the difference between an interface and an abstract class.
What I am actually looking for, is a real world example that can help highlight the difference between the two.
The player Interface
In my Java courses I often use this kind of image and ask: "What is this ?"
Every time someone will say "that's a player". From this image you can teach anybody what an interface is. This Interface allow any user to "play" something. Everybody knows what these buttons mean, even if you don't know what exactly will be done, you can use anything with this interface and you know that the little arrow will "play" and other arrows will probably send you forward or backward.
Everything that will have those buttons will provide a standard behavior that any user will know before even starting to use them.
I usually try to avoid the "contract" word which can be misunderstood.
The DVD player Abstract class
And then from the Play Interface, I go to the VCR (or DVD) player. Every constructor of DVD player must add some special functions to transform a simple unknown player into a DVD player. For example the eject button. And they must correctly implement Player.
The play button will launch the content of the DVD.
But even if DVD Player provide the basic behavior of a DVD player, not everything is done. You can't simply have "a" DVD player, it has a brand and most of the time it has its own firmware. A this time you'll need to extend the DVD Player abstract class to add your own little components.
Here's a good comparison of the two: interface vs abstract class. I've copied a specific example from there below:
Interface
Interfaces are often used to describe the peripheral abilities of a class, not its central identity, e.g. An Automobile class might implement the Recyclable interface, which could apply to many otherwise totally unrelated objects.
Abstract class
An abstract class defines the core identity of its descendants. If you defined a Dog abstract class then Dalmatian descendants are Dogs, they are not merely dogable. Implemented interfaces enumerate the general things a class can do, not the things a class is.
Interface
An interface is simply a specification. It describes what something MUST do. Nothing more, nothing less. On its own, it is meaningless. It is only useful when someone takes that specification and implements it.
Think of a USB memory stick. It conforms to the specifications of USB. A device communicating with it doesn't need to know or care how the memory stick is going about its job, all it needs to know is that when we ask for data to be written, it is written; conversely, when we ask to read data from it we expect to receive the data.
In computing terms, we use an interface in the same way. If we have:
public interface IUSB
{
Data Read();
bool Write(Data data);
}
We know that anything implementing this interface has to provide an implementation for Read and Write. How or what it does behind the scenes is of no concern to us. By passing an interface around our code we're not tying ourselves down to specific implementations.
Abstract Class
An Abstract Class simply provides us with a means to put in place specification in a base class that derived types must implement, as well as common code that can be used by all derived types.
I've been trying to thing of a good real-world example and have struggled, so can only really come up with a code example.
Say you wanted to implement an employee hierarchy in your code. So you may have:
public abstract class Employee
{
public string FirstName { get; protected set; }
public string LastName { get; protected set; }
public string Grade { get; protected set; }
public int Salary { get; protected set; }
public abstract void GivePayRise();
}
Every employee has a name and an associated job grade. We can model this in the base class with the first 3 properties. However, giving a bonus may not be a straightforward affair, depending on grade etc. So, we mark this as abstract. Every derived type of Employee (Part-Time, Full-Time, Contract, Consultant) has to implement this.
An implementation may be:
public class FullTimeEmployee : Employee
{
public void GivePayRise()
{
Salary *= 1.1;
}
}
public class PartTimeEmployee : Employee
{
public void GivePayRise()
{
Salary *= 1;
}
}
So we want to give a 10% raise to full-time employees, but nothing to part-time ones.
Difficult to give good examples - I generally tend to use interfaces, can't really remember in the past year or so when I've used an abstract class. This could start the whole Abstract Class vs Interface debate, but that's a whole new page.......
For everything computer related, I use a cooking dinner example. I start by saying that hard drives are cabinets/storage closets. Memory is like your counter. Processor is the cooking apparatus (stove). You are like the system bus (moving things around, etc...). So when you boot a computer, you take your basic ingredients out of storage and put them on the counter (loading the OS). This is a loose example, but it works well.
Now to move into OOP: an ingredient is an object, so is a tool (bowl, knife, spoon, etc...). Each one of these has properties (knife= handle_color: black, blade_type: serrated, etc...). And each one has methods/actions that you can perform with them (knife = cut(pepper)).
Now you can take this as far as you want to. For instance, there are green, yellow and red peppers. Each one is a pepper, so you can say "inherit the pepper class" (layman: take everything you know about a pepper and apply it to this specific pepper, pepper has a color attribute, a red pepper is color=red).
You can even separate class from instance (this particular pepper is an instance, whereas on the recipe card it's a class).
So you could make some pseudocode:
class pepper {
var color
var spiciness
var size
}
class redPepper extends pepper {
construct(){
$this->color=red
}
}
class cuttingKnife extends knife{
construct(){
$this->blade_type=serated
}
}
class cookingPot extends pot{
construct(){
//We all know what a cooking pot is
}
}
class stove extends apparatus{
construct(){
//We all know what a stove is
}
}
$knife = new cuttingKnife();
$myPepper = new redPepper();
$pot = new cookingPot();
$stove = new stove();
$knife->cut($myPepper);
$pot->putOn($stove);
$stove->cookOn("high");
$pot->putIn("water");
$pot->putIn($myPepper);
//This will boil a cut pepper
Of course, people won't necessarily understand the pseudocode, but they would understand how to boil something. They would understand the difference between a "pepper" and a "red pepper". I think you can pretty much use this analogy for any computer related thing with some minor tweeks.
multithreading: add more burners to the stove and another cook in a single kitchen
multicore arch.: add a second kitchen
downloading/installing software: go to store, find food, bring home, deposit in storage
partitioning a HDD: different cabinets/fridge could be Linux proc system (because it's special).
Etc...
Interface: The buttons of the remote control. Users know how these buttons are supposed to function.
Concrete class: Toshiba RC, Philips RC, JVC RC - what's inside the box is the concrete implementation.
Abstract class: The stencil a tailor uses in order to create a Made to measure garment. While You can't wear the stencil itself it is used to produce suits You can wear - the suits are "derived" from the stencil.
Interface: A dress code.
A good example is a calculator. Inside a calculator is a circuit board that has connections between its display, buttons, and a logic processor.
The circuit board acts like an abstract class. It provides the plumbing for any calculator built with it. In addition, it has certain interfaces that connect to a display, to an array of buttons, and to a logic processor.
In turn, any display manufactured to work with the circuit board must have a connector that fits the display interface on the circuit board. The same goes for the buttons and the logic processor, the latter likely having a certain arrangement of pins that align with the interface on the circuit board.
A developer using OOD would create an abstract class, CalculatorBase, to define the plumbing between the buttons, the display, and the internal logic. The abstract class would also specify how derivative classes use this plumbing to respond to certain events.
CalculatorBase, however, wouldn't depend on a specific display, a specific set of buttons, or even a specific implementation of logic. Instead, the developer specifies an interface for each, such as ICalculatorDisplay, for example. ICalculatorDisplay would specify how CalculatorBase expects to interact with a display. CalculatorBase would then work with any display that implements ICalculatorDisplay.
(In some languages, abstract class is used the same way as an interface, so it may be confusing)
A handful of classes that all have a certain common interface is like a handful of words that can fill in the the blank in a sentence. Example:
____ has wings
Chicken has wings
Airbus A320 has wings
However, the classes themselves, while they can all fit the blank in the sentence, do not have any relatioship in between. Chicken is a fowl while Airbus A320 is an aircraft. The only commonality is that they both have something that we call "wings". (You can also say that the true meanings of the "wings" are different in the two situations.)
class IHasWings : public IUnknown
{
public:
// IUnknown methods: (Inherited)
// IHasWings methods:
virtual HRESULT GetWingSpan([out] double* pdblWingSpan) = 0;
virtual HRESULT IsWingMovable([out] BOOL* pIsMovable) = 0;
virtual HRESULT IsWingDetachable([out] BOOL* pIsDetachable) = 0;
};
class Chicken : public ... ..., public IHasWings
{
};
class AirbusA320 : public ... ..., public IHasWings
{
};
Very simply put, an interface defines how you can talk to me.
Whereas an abstract class could define one of my talents such as playing the guitar. The problem is that "playing guitar," by itself isn't really that useful. But we could use this ability to create a type of person, such as a musician (which we could say is a class).
The Open/Closed Principle states that software entities (classes, modules, etc.) should be open for extension, but closed for modification. What does this mean, and why is it an important principle of good object-oriented design?
It means that you should put new code in new classes/modules. Existing code should be modified only for bug fixing. New classes can reuse existing code via inheritance.
Open/closed principle is intended to mitigate risk when introducing new functionality. Since you don't modify existing code you can be assured that it wouldn't be broken. It reduces maintenance cost and increases product stability.
Specifically, it is about a "Holy Grail" of design in OOP of making an entity extensible enough (through its individual design or through its participation in the architecture) to support future unforseen changes without rewriting its code (and sometimes even without re-compiling **).
Some ways to do this include Polymorphism/Inheritance, Composition, Inversion of Control (a.k.a. DIP), Aspect-Oriented Programming, Patterns such as Strategy, Visitor, Template Method, and many other principles, patterns, and techniques of OOAD.
** See the 6 "package principles", REP, CCP, CRP, ADP, SDP, SAP
More specifically than DaveK, it usually means that if you want to add additional functionality, or change the functionality of a class, create a subclass instead of changing the original. This way, anyone using the parent class does not have to worry about it changing later on. Basically, it's all about backwards compatibility.
Another really important principle of object-oriented design is loose coupling through a method interface. If the change you want to make does not affect the existing interface, it really is pretty safe to change. For example, to make an algorithm more efficient. Object-oriented principles need to be tempered by common sense too :)
Open Closed Principle is very important in object oriented programming and it's one of the SOLID principles.
As per this, a class should be open for extension and closed for
modification. Let us understand why.
class Rectangle {
public int width;
public int lenth;
}
class Circle {
public int radius;
}
class AreaService {
public int areaForRectangle(Rectangle rectangle) {
return rectangle.width * rectangle.lenth;
}
public int areaForCircle(Circle circle) {
return (22 / 7) * circle.radius * circle.radius;
}
}
If you look at the above design, we can clearly observe that it's not
following Open/Closed Principle. Whenever there is a new
shape(Tiangle, Square etc.), AreaService has to be modified.
With Open/Closed Principle:
interface Shape{
int area();
}
class Rectangle implements Shape{
public int width;
public int lenth;
#Override
public int area() {
return lenth * width;
}
}
class Cirle implements Shape{
public int radius;
#Override
public int area() {
return (22/7) * radius * radius;
}
}
class AreaService {
int area(Shape shape) {
return shape.area();
}
}
Whenever there is new shape like Triangle, Square etc. you can easily
accommodate the new shapes without modifying existing classes. With
this design, we can ensure that existing code doesn't impact.
Software entities should be open for extension but closed for modification
That means any class or module should be written in a way that it can be used as is, can be extended, but neve modified
Bad Example in Javascript
var juiceTypes = ['Mango','Apple','Lemon'];
function juiceMaker(type){
if(juiceTypes.indexOf(type)!=-1)
console.log('Here is your juice, Have a nice day');
else
console.log('sorry, Error happned');
}
exports.makeJuice = juiceMaker;
Now if you want to add Another Juice type, you have to edit the module itself, By this way, we are breaking OCP .
Good Example in Javascript
var juiceTypes = [];
function juiceMaker(type){
if(juiceTypes.indexOf(type)!=-1)
console.log('Here is your juice, Have a nice day');
else
console.log('sorry, Error happned');
}
function addType(typeName){
if(juiceTypes.indexOf(typeName)==-1)
juiceTypes.push(typeName);
}
function removeType(typeName){
let index = juiceTypes.indexOf(typeName)
if(index!==-1)
juiceTypes.splice(index,1);
}
exports.makeJuice = juiceMaker;
exports.addType = addType;
exports.removeType = removeType;
Now, you can add new juice types from outside the module without editing the same module.
Let's break down the question in three parts to make it easier to understand the various concepts.
Reasoning Behind Open-Closed Principle
Consider an example in the code below. Different vehicles are serviced in a different manner. So, we have different classes for Bike and Car because the strategy to service a Bike is different from the strategy to service a Car. The Garage class accepts various kinds of vehicles for servicing.
Problem of Rigidity
Observe the code and see how the Garage class shows the signs of rigidity when it comes to introducing a new functionality:
class Bike {
public void service() {
System.out.println("Bike servicing strategy performed.");
}
}
class Car {
public void service() {
System.out.println("Car servicing strategy performed.");
}
}
class Garage {
public void serviceBike(Bike bike) {
bike.service();
}
public void serviceCar(Car car) {
car.service();
}
}
As you may have noticed, whenever some new vehicle like Truck or Bus is to be serviced, the Garage will need to be modified to define some new methods like serviceTruck() and serviceBus(). That means the Garage class must know every possible vehicle like Bike, Car, Bus, Truck and so on. So, it violates the open-closed principle by being open for modification. Also it's not open for extension because to extend the new functionality, we need to modify the class.
Meaning Behind Open-Closed Principle
Abstraction
To solve the problem of rigidity in the code above we can use the open-closed principle. That means we need to make the Garage class dumb by taking away the implementation details of servicing of every vehicle that it knows. In other words we should abstract the implementation details of the servicing strategy for each concrete type like Bike and Car.
To abstract the implementation details of the servicing strategies for various types of vehicles we use an interface called Vehicle and have an abstract method service() in it.
Polymorphism
At the same time, we also want the Garage class to accept many forms of the vehicle, like Bus, Truck and so on, not just Bike and Car. To do that, the open-closed principle uses polymorphism (many forms).
For the Garage class to accept many forms of the Vehicle, we change the signature of its method to service(Vehicle vehicle) { } to accept the interface Vehicle instead of the actual implementation like Bike, Car etc. We also remove the multiple methods from the class as just one method will accept many forms.
interface Vehicle {
void service();
}
class Bike implements Vehicle {
#Override
public void service() {
System.out.println("Bike servicing strategy performed.");
}
}
class Car implements Vehicle {
#Override
public void service() {
System.out.println("Car servicing strategy performed.");
}
}
class Garage {
public void service(Vehicle vehicle) {
vehicle.service();
}
}
Importance of Open-Closed Principle
Closed for modification
As you can see in the code above, now the Garage class has become closed for modification because now it doesn't know about the implementation details of servicing strategies for various types of vehicles and can accept any type of new Vehicle. We just have to extend the new vehicle from the Vehicle interface and send it to the Garage. That's it! We don't need to change any code in the Garage class.
Another entity that's closed for modification is our Vehicle interface.
We don't have to change the interface to extend the functionality of our software.
Open for extension
The Garage class now becomes open for extension in the context that it will support the new types of Vehicle, without the need for modifying.
Our Vehicle interface is open for extension because to introduce any new vehicle, we can extend from the Vehicle interface and provide a new implementation with a strategy for servicing that particular vehicle.
Strategy Design Pattern
Did you notice that I used the word strategy multiple times? That's because this is also an example of the Strategy Design Pattern. We can implement different strategies for servicing different types of Vehicles by extending it. For example, servicing a Truck has a different strategy from the strategy of servicing a Bus. So we implement these strategies inside the different derived classes.
The strategy pattern allows our software to be flexible as the requirements change over time. Whenever the client changes their strategy, just derive a new class for it and provide it to the existing component, no need to change other stuff! The open-closed principle plays an important role in implementing this pattern.
That's it! Hope that helps.
It's the answer to the fragile base class problem, which says that seemingly innocent modifications to base classes may have unintended consequences to inheritors that depended on the previous behavior. So you have to be careful to encapsulate what you don't want relied upon so that the derived classes will obey the contracts defined by the base class. And once inheritors exist, you have to be really careful with what you change in the base class.
Purpose of the Open closed Principle in SOLID Principles is to
reduce the cost of a business change requirement.
reduce testing of existing code.
Open Closed Principle states that we should try not to alter existing
code while adding new functionalities. It basically means that
existing code should be open for extension and closed for
modification(unless there is a bug in existing code). Altering existing code while adding new functionalities requires existing features to be tested again.
Let me explain this by taking AppLogger util class.
Let's say we have a requirement to log application wide errors to a online tool called Firebase. So we create below class and use it in 1000s of places to log API errors, out of memory errors etc.
open class AppLogger {
open fun logError(message: String) {
// reporting error to Firebase
FirebaseAnalytics.logException(message)
}
}
Let's say after sometime, we add Payment Feature to the app and there is a new requirement which states that only for Payment related errors we have to use a new reporting tool called Instabug and also continue reporting errors to Firebase just like before for all features including Payment.
Now we can achieve this by putting an if else condition inside our existing method
fun logError(message: String, origin: String) {
if (origin == "Payment") {
//report to both Firebase and Instabug
FirebaseAnalytics.logException(message)
InstaBug.logException(message)
} else {
// otherwise report only to Firebase
FirebaseAnalytics.logException(message)
}
}
Problem with this approach is that it violates Single Responsibility Principle which states that a method should do only one thing. Another way of putting it is a method should have only one reason to change. With this approach there are two reasons for this method to change (if & else blocks).
A better approach would be to create a new Logger class by inheriting the existing Logger class like below.
class InstaBugLogger : AppLogger() {
override fun logError(message: String) {
super.logError(message) // This uses AppLogger.logError to report to Firebase.
InstaBug.logException(message) //Reporting to Instabug
}
}
Now all we have to do is use InstaBugLogger.logError() in Payment features to log errors to both Instabug and Firebase. This way we reduce/isolate the testing of new error reporting requirement to only Payment feature as code changes are done only in Payment Feature. The rest of the application features need not be tested as there are no code changes done to the existing Logger.
The principle means that it should easy to add new functionality without having to change existing, stable, and tested functionality, saving both time and money.
Often, polymorhism, for instance using interfaces, is a good tool for achieving this.
An additional rule of thumb for conforming to OCP is to make base classes abstract with respect to functionality provided by derived classes. Or as Scott Meyers says 'Make Non-leaf classes abstract'.
This means having unimplemented methods in the base class and only implement these methods in classes which themselves have no sub classes. Then the client of the base class cannot rely on a particular implementation in the base class since there is none.
I just want to emphasize that "Open/Closed", even though being obviously useful in OO programming, is a healthy method to use in all aspects of development. For instance, in my own experience it's a great painkiller to use "Open/Closed" as much as possible when working with plain C.
/Robert
This means that the OO software should be built upon, but not changed intrinsically. This is good because it ensures reliable, predictable performance from the base classes.
I was recently given an additional idea of what this principle entails: that the Open-Closed Principle describes at once a way of writing code, as well as an end-result of writing code in a resilient way.
I like to think of Open/Closed split up in two closely-related parts:
Code that is Open to change can either change its behavior to correctly handle its inputs, or requires minimum modification to provide for new usage scenarios.
Code that is Closed for modification does not require much if any human intervention to handle new usage scenarios. The need simply does not exist.
Thus, code that exhibits Open/Closed behavior (or, if you prefer, fulfills the Open/Closed Principle) requires minimal or no modification in response to usage scenarios beyond what it was originally built for.
As far as implementation is concerned? I find that the commonly-stated interpretation, "Open/Closed refers to code being polymorphic!" to be at best an incomplete statement. Polymorphism in code is one tool to achieve this sort of behavior; Inheritance, Implementation...really, every object-oriented design principle is necessary to write code that is resilient in the way implied by this principle.
In Design principle, SOLID – the "O" in "SOLID" stands for the open/closed principle.
Open Closed principle is a design principle which says that a class, modules and functions should be open for extension but closed for modification.
This principle states that the design and writing of the code should be done in a way that new functionality should be added with minimum changes in the existing code (tested code). The design should be done in a way to allow the adding of new functionality as new classes, keeping as much as possible existing code unchanged.
Benefit of Open Closed Design Principle:
Application will be more robust because we are not changing already tested class.
Flexible because we can easily accommodate new requirements.
Easy to test and less error prone.
My blog post on this:
http://javaexplorer03.blogspot.in/2016/12/open-closed-design-principle.html