Friday, March 20, 2009

Data Hiding

Today my blog is not directly related with eclipse. Lets have an OO discussion.

This started with a thought provoking arguement with one of my colleague. 

Problem - We have a public method which needs to return a data structure, say a 'List'. Which method signature attributes to a better design. (a) or (b)

(a) public List getRecords();
(b) public RecordCollectionVO getRecords();, where RecordCollectionVO is an object which internally stores a List instance.

Lets analyse both the cases. 

Arguements in favour of (a)
  1. The method is implemented based on design by contract.
  2. An extra class just to hold a list is an overhead.
  3. Tomorrow if the 'List' changes to a Map, anyway the module which access this module needs to make a few code changes. Then what the heck in changing the method signature as well. Why are you lazy to change one extra line of code?
  4. The chances for the data structure 'List' to change are rare.
Arguements in favour of (b)
  1. The method returns an object which hides the internal data structure which it uses.
  2. Tomorrow if the methods wants to return a Map instead of a List, the method signature will be unaffected. Hence which ever module that invokes this method doesn't need to change the way it access this method. 
I would opt for (b). Why?

Lets analyse a scenario which addresses both cases.

Case (a)

Consider three modules X, Y, Z.

Module X has the following method.
public List getRecords();

Module Y accesses module X and delegates the work to Module Z.
List list = moduleX.getRecords();
moduleZ.modify(list);

ModuleZ has the following method.
public void modify(List list);

I get a change request from my customer. I need to change the datastructure from List to a Map. In this case I need to modify the method signature, which inturn forces me to modify module X, module Y and module Z.

Case (b)

Consider three modules X, Y, Z.

Module X has the following method.
public RecordCollectionVO getRecords();

Module Y access module X and delegates the work to Module Z.
RecordCollectionVO rcVO = moduleX.getRecords();
moduleZ.modify(rcVO);

ModuleZ has the following method.
public void modify(RecordCollectionVO rcVO);

Now I need to change the datastructure List used inside RecordCollectionVO to a Map. In this case I do not need to modify the method signature. Thats good. But I have something more important here. I do not need to change even a single line of code in Module Y. The modules which need code changes are Module X and Module Z. 

If I can save atleast a single module from a single line of code change, I do not need to test that module again.

What ever, these are just two schools of thought. 

7 comments:

  1. The analysis should include the API implemented by RecordCollectionVO because it's not clear how the list verses map decision is hidden by that API.

    ReplyDelete
  2. 1. Use a Collection, not a list.
    2. Use an interface to abstract away the RecordCollectionVO
    3. Combine those: Collection<IRecordCollectionVO>

    Make sure you only use that for public APIs. No need to make life harder everywhere.

    ReplyDelete
  3. Hello Ed,

    public class RecordCollectionVO {
    private List records;

    public List getRecords() {
    return records;
    }

    }

    When the client module requests Module X for records, Module X will wrap the records into a RecordCollectionVO object and gives to the client.

    ReplyDelete
  4. Why don't you just do

    moduleZ.modify(moduleX.getRecords());

    and not worry?

    If Module Y doesn't need to know anything about what getRecords() returns so no need to set a variable.

    ReplyDelete
  5. I would ask why is the object returning a List of records and therefore breaking encapsulation. Something is doing something with the list, could the object perform that task instead?

    Alternatively, the object have a foreachRecord method which takes an interface which is called with each record in turn?

    It depends on context but I would avoid breaking encapsulation if at all possible.

    ReplyDelete
  6. Patrik,

    I avoid

    moduleZ.modify(moduleX.getRecords());

    because moduleZ is not aware about moduleX. I should have put more details in the blog.

    Channing Walton,
    Did you mean why module X needs to return a list? Good question! I will think about it.

    ReplyDelete
  7. Yes (why return the list). I often find that when behaviour is pushed 'into' objects things get simpler, ie Tell Don't Ask.

    ReplyDelete