I have the following interface (some methods ommited for simplicity):
public interface Container{
public void put(String s, Object o);
public Object get(String s);
}
and its implementation:
public class ContainerImpl implements Container{
private Map<Stirng, Object> m;
public ContainerImpl(){
m = new HashMap<>();
}
//This constructor is used in the case if a client is not satisfied
//by the HashMap, e.g. in case of Enum it's better to use EnumMap
protected ContainerImpl(Map<String, Object> m){
this.m = m;
}
public void put(String s, Object o){
m.put(s, o);
}
public Object get(String s){
m.get(s);
}
}
My question is about if providing such a protected constructor contraries to incapsulation. In fact we give to clients some knowledge that internally we use Map. If the dataStructure changed, we'll have to perform a conversion from the map passed as a parameter which may probably cause some bugs, I think.
if providing such a protected constructor contraries to incapsulation.
You are right, it does contradicts incapsulation behavior of ContainerImpl.
IMHO this is a design decision; whether class is designed to enforce incapsulation or to expose to client's/caller's for supporting varities of constructs.
For example:
A: ContainerImpl with only default-constructor implies that internal storage of Container is completely governed by it's concrete-implementation and caller cannot choose different storage.
B:
And ContainerImpl with
protected ContainerImpl(Map<String, Object> m)
implies that caller can choose the nature of Map based storage i.e. TreeMap, HashMap, LinkedHashMap or a custom implementation.
Decision on choosing one of the above approached would be based on client's need and nature.
You have responsibilities of creation, use and encapsulation of underlying Map in a single class. If you want to follow SRP, try leaving the only public constructor which accepts Map as argument, use factories or descendants to encapsulate data:
/**
* @param m - storage model. Should not be modified after call.
*/
public ContainerImpl(Map<String, Object> m){
this.m = m;
}
/** A new instance with default storage model */
public static ContainerImpl createDefault() {
// Storage reference is isolated
return new ContainerImpl(new HashMap<>());
}
Alternatively, delete all constructors and provide:
protected abstract Map<String, Object> getStorage();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With