Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I protect my class's implementation from other developers?

Consider this interface

public interface IDoSomething {
   void DoAAA();
   void DoBBB();
   void DoCCC();
}

and these two implementations

public class MyObject1 implements IDoSomething {
   public MyObject(int a, xObjx x)
    ..
}   

public class MyObject2 implements IDoSomething {
    public MyObject(int a, xObjx x)
    ..
}   

How can I not expose the implementation classes to other developers? Specifically how can I prevent the following?

IDoSomething C = new  MyObject2();

Is using a factory class the best practice?

public class DoSomethingFactory {
    static IDoSomething getDoSomething(int a, xObjx x) {
        ...
    }
}

How can I extend this pattern to include implementations that have different constructors?

like image 803
Bick Avatar asked Oct 21 '22 08:10

Bick


2 Answers

If you're always going to be constructing the MyObject1/2 instances in your own code, you might be able to make them package private (default permissions). A "builder" pattern might make sense too.

Can you maybe elaborate on how / why it matters if the constructors of the objects are exposed? Interfaces just define functionality requirements, not construction requirements.

like image 132
David Avatar answered Oct 23 '22 03:10

David


Good software is written on a need to know basis. The more ignorant a component is about how its companion classes get the job done the better. That is purpose that interfaces serve. They allow willfully ignorant programmers to write code on a need to know basis.

One problem is a programmer that wants to know can almost always know. Even if you could prevent the assignment which you can't, they can cast the obfuscated object into its implementation, inspect its properties and methods with reflection, or pipe the jar into a hex editor. This isn't really something you can stop.

What you can do is write code that makes the lives of need to know programmers easier, clearly communicates the intention of your design, and reduces the temptation for programmers to cheat(using dirty implementation details as a crutch). The best way accomplish this is to use Creational Patterns.

In reference to your point about constructors being different this is handled nicely by all the basic creational patterns. Because they provide an abstraction over the constructor. You get to decide how much or how little detail to expose. Maybe you have a constructor like the one below:

public ColorThing(int r ,int g, int b)

that accepts numeric color values. In your implementation of a creational pattern you might decide to simplify this by making the constructor private and only allowing access to a static factory method.

private ColorThing(int r , int g, int b){
    ...
}
public static ColorThing buildRedColorThing(){
    return new ColorThing(100,0,0);
}

This would limit consumers of your ColorThing class to only being able to construct red things.

Listed below are several basic creational patterns that sort of do what you want. They provide varying degrees of obfuscation building up to the Abstract Factory Pattern which yields the highest degree of encapsulation but also requires the most boiler plate.

Static Factory Method

This is the guy from the ColorThing example above. This simply provides a public static method on the class that protects the nitty gritty details of the constructor. Great for simplifying access to a constructor with lots of parameters. In this case, you can make your constructor private and provide as many static factory methods as you need.

public class MyObject1 implements IDoSomething {
    private int prop a;
    public static IDoSomething getAnObject(int a){
       return new MyObject(a, new xObjx());
    }
    private MyObject(int a, xObjx x)
        ...
}

The Builder Pattern

The builder pattern has an accompanying class responsible for creating your class. It uses java's scope rules to ensure that the constructor is accessed in a secure fashion. This is also very useful if you want to have a lot of telescoping constructor args because you can rely on dot chaining of the methods of the builder class (new MyObject1.MyObjectBuilder().setA(2).build())

public class MyObject1 implements IDoSomething {
   private int prop a;
   private MyObject1(int a, xObjx x)
   ...

   public class MyObjectBuilder {
       private int a;

       public MyObjectBuilder setA(int a){
            this.a = a;
            return this;
       }
       public IDoSomething build(){
           return new MyObject1(a, new xObjx); 
       }
    }
}

Abstract Factory

The Abstract Factory Pattern allows you to completely encapsulate the construction of a class. This will only every expose the interface, but it also has the most boiler plate because you have to create a Factory and a set of Product implementations and interfaces for each family of classes you want to manufacture. Here is a text book example of how the classes in this pattern relate. This pattern is highly extensible and you will see it used to great effect in lots of library code. This is because the designers of these libraries commonly don't know the full set of implementations their framework code will need to support. By using an Abstract Factory Pattern they can let future developers build on their code while limiting the restrictions imposed by their assumptions.

Which one you pick will be entirely contingent on your circumstances, but you should start with the simplest one that satisfies your needs and work your way to the more complex ones.

like image 36
nsfyn55 Avatar answered Oct 23 '22 02:10

nsfyn55