for example, I have some Fruits:
Fruit.h
#ifndef __Fruit__
#define __Fruit__
#include <string>
class Fruit{
public:
virtual void hi(std::string username)=0;
};
#endif
Apple.h
#include "Fruit.h"
#include <stdio.h>
class Apple : public Fruit{
public:
virtual void hi(std::string message){
printf("Hi %s,I am apple\n",message.c_str());
}
};
Orange.h
#include "Fruit.h"
#include <stdio.h>
class Orange : public Fruit{
public:
virtual void hi(std::string message){
printf("Hi %s,I am orange\n",message.c_str());
}
};
I need to decide which to use according to input string:
#include "Apple.h"
#include "Orange.h"
int main(){
std::string username="abc";
std::string input="Orange";
if(input=="Apple"){
Apple().hi(username);
}else if(input=="Orange"){
Orange().hi(username);
}
return 0;
}
I know it doesn't obey open closed principle because adding a new Fruit needs to add a new if-else condition to map to the correct function. I heard registry pattern can make this case obey open closed principle, is it true? if so, how to implement registry pattern here?
This can become quite verbose codewise so I use words instead. A definition of the registry pattern :
A registry is a global association from keys to objects, allowing the objects to be reached from anywhere. It involves two methods: one that takes a key and an object and add objects to the registry and one that takes a key and returns the object for the key
The thing here is that the registry doesn't know how to build the object, just how to retrieve it. This is a significant difference to the creational patterns.
Basically your fruit should can change to
#include <string>
class Fruit{
public:
virtual std::string key() const = 0;
virtual void hi(std::string username) = 0;
};
And now you introduce a registry
class FruitRegistry final {
public:
bool register(Fruit* fruit);
Fruit* locate(std::string key);
private:
std::map<string, Fruit*> registry;
};
The means of registering/retrieving fruits should be the same whatever the fruit. Here it can be done using a map string to fruit. You can also design the class fruit so it uses an accept method, which is quite useful when the input is complex (think initializing a fruit depending on its description).
When it is useful ? When it is used behind an interface to access a type of resources, like img.load("cats.jpg")
. The format jpg, bmp, png are quite different and might need a separate engine for each, or one for jpg, the other for both bmp and png. But the user doesn't care about those details. Note that more and more images type can be added in the future without touching the image loading.
The issue is that to provide the nice img.load("cats.jpg")
the registry mechanism underneath can be complex to design. You need to ask yourself :