Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding a protected constructor to allow subclasses provide different implementations

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.

like image 791
St.Antario Avatar asked Jun 19 '26 10:06

St.Antario


2 Answers

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.

like image 113
harsh Avatar answered Jun 20 '26 23:06

harsh


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();
like image 24
Basilevs Avatar answered Jun 20 '26 23:06

Basilevs



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!