Sunday, January 1, 2012

Customize class using methods rather than constant fields


Suppose you are going to have two similar classes that differ only by some behaviours and parameters. For instance, Reader and Writer both do some activity: reader reads, writer writes. Let's make them descendants of Communicator class with transfer() method. The Reader and Writer classes will override this method in their specific way. The implementation will be shared among all instances of reader and writer classes. Now, reader must respond with "reader" when his title is requested but writer must respond that he is "writer". How do you implement this?

You may create an "abstract" field and initialize it in the constructor

class Communicator {

  String param = "transferred"; // default value, to be "overriden"

  Communicator(param) { // constructor
    this.param = param; 
  }
  abstract transfer(buf[], size); //
}


class Reader extends Communictor {
  Reader() {super("read")}; // overriding the report message
  void transfer(buf[], size) {
    read(buf, size); 
  }
 

This will allocate a string param for every instance of Reader and Writer. On the other hand, you can parametrize the op name through a method:

class Communicator {
  abstract void transfer (buf[], size);
  abstract String param(); // to be overriden

Then, to create a Reader,


class Reader extends Communicator() {
  void transfer(buf[], size) {
    read(buf, size); 
  }
  String param() {
    return "read";
  }


Method overriding seems to be more wordy, it demands you to define a method for every parameter you what to customize. Yet, you are a programmer and want to minimize your effort. You'll prefer the solution with field and constructor, right? Not right. The problem is that The less fields the object has, the smaller is its memory footprint!

It is important to have small footprint for the objects that you may have in numbers.

Recall that a method is created only once per class in runtime and shared among all instances of the class. It does not take space. Meantime, the fields are elements of the (java) data objects. Objects are basically arrays of the fields (as it was in structural programming). It is bad to replicate the same info and it is what you do if define the same value parameter to all "readers". I understand that "String param" is only a reference. Yet it occupies 4 bytes (actually 8 in 64-bit systems) and is very bad to replicate the same constant information in all instances of your class if you already have a reference to that information through the (getClass) pointer. Using variables to store known constants, replicating well-known constants all over the place is a bad idea. It does not make sense to keep info in system memory unless you use it often. And, though Java provides class-wide fields, you cannot declare static in base class and initialize it in subclasses so that all Readers share one static message, Writers have another.

So, if all objects of a given class share the same parameter, override a method. I call it "sharing across class instances". If parameter differs from object to object, introduce a filed. A practical example would be a decision between report and assertion statements in VHDL. Here, report is like an assertion, which always have its condition false and different prefix.

 class Report(report_msg, severity_level) {
    String getPrefix() {return "Report";} // to be overriden
 }
 class Assertion extends Report(report_msg, severity_level, condition) { 
    String getPrefix() {return "Assertion Failed";} // overriden
 
Odersky has highly simplified the definition of methods so that it is almost as much fun as passing an argument into the constructor. I dream of the shared attributes-based subclassing. Also, read about problems with initializers in java.
 

No comments: