Where do objects come from? III

27 Mar 2016

Kent Beck has a series of articles, describing various origins of objects, "Objects from States", "Objects from Variables", "Objects from Collections", "Objects From Methods".

I think it's an interesting question, and Kent Beck didn't exhaust it.

Let's suppose that there is some piece of code which is resolutely procedural.

Michael A. Jackson has this example (in "Designing and Coding Program Structures"):

A serial file contains records of issues and receipts of parts in a factory store: the records have been sorted into ascending order by part-number. A summary is to be produced showing the net movement for each part; the summary has a simple heading line, and an ending line showing the number of parts for which movements have occurred in the period of the summary.

In C or C++, an entirely procedural solution might look something like this:

void process_movements_giving_summary() {
  process_headline();
  process_report_body();
  process_endline();
}

void process_headline() {
  [...]
}

void process_report_body() {
  while ([...]) {
    process_partgroup();
    process_net_movement_line();
  }
}

void process_partgroup() {
  while ([...]) {
    process_movement_record();
  }
}

void process_movement_record() {
  if ([...]) {
    process_issue();
  }
  if ([...]) {
    process_receipt();
  }
}

void process_issue() {
  [...]
}

void process_receipt() {
  [...]
}

void process_net_movement_line() {
  [...]
}

void process_endline() {
  [...]
}

If we built this successfully for one customer, and then adapted it to another customer's requirements, then we might end up with two similar-but-different systems.

Let's assume that the basic skeleton is identical, and functions named process_headline, process_endline, process_movement_record, and process_net_movement_line exist in both systems, but that they differ in their implementations.

Using polymorphic objects, you can nearly thoughtlessly factor out the common part:

class CustomerInterface {
  public:
    virtual void process_headline() = 0;
    virtual void process_endline() = 0;
    virtual void process_movement_record() = 0;
    virtual void process_net_movement_line() = 0;
    
    virtual ~CustomerInterface() {}
  protected:
    CustomerInterface() {}
};

class FirstCustomer : public CustomerInterface {
  public:
    void process_headline();
    void process_endline();
    void process_movement_record();
    void process_net_movement_line();
};

class SecondCustomer : public CustomerInterface {
  public:
    void process_headline();
    void process_endline();
    void process_movement_record();
    void process_net_movement_line();
};

void process_movements_giving_summary(Customer* c) {
  c->process_headline();
  process_report_body(c);
  c->process_endline();
}

void process_report_body(Customer* c) {
  while ([...]) {
    process_partgroup(c);
    c->process_net_movement_line();
  }
}

void process_partgroup(Customer* c) {
  while ([...]) {
    c->process_movement_record();
  }
}

This is an example of the Strategy pattern, I guess? The totalitarian "everything ought to be an object" rhetoric (from around 1990?), obscures discussion of: suppose you didn't have any objects. What would prompt you to introduce an object? I think this is how it would go.

There's another aspect that I've neglected in this post, which is: why are objects different from records of functions?

I believe this is answered by some differences from one customer to another could have been dealt with via data (data-polymorphism) and only the remaining differences (differences in procedures) actually need the records-of-functions approach.

So if you're committed to handling both kinds of differences, then you get records of both data and functions, where the functions take as one of their arguments this record of data and functions - that is, objects.

However, there is a different aspect that I'm still neglecting, even with this breezy and handwaving argument about data-polymorphism, which is: why are the data fields of objects mutable?

I don't yet have a good story about why objects should be mutable;