M
masood.iqbal
[My apologies if this LONG posting is off-topic]
In this day and age, you never say no to any work that is thrown at you
---- so when I was offered this short-term contract to convert legacy C
code to C++, I did not say no. Personally I believed that it was a
somewhat futile exercise since one of the main requirements was for the
existing API (a functional interface written in C) to remain the same.
I would have much rathered that the mandate be ab-initio, but that was
not the case here. My client had a bad experience with OO, and they
wanted to re-tread this path very very carefully. They were convinced
that a "phased approach" to OO worked much better for them.
I tried to argue (although not very passionately) that functional and
OO design together provide a rather immiscible combination --- one
which nobody is happy with. However, for someone who was prepared to
dig trenches, I did not make a very big deal out of it. Since the API
had to remain the same, and there was not enough time for a detailed
use-case/OO analysis, here's how we got about this task.
1) Studied the current implementation of the APIs --- if it sounded
"coherent", and if it could be easily converted into a method on a
class, we went ahead and did that
2) If the API implementation did bot seem coherent, we chopped it into
coherent parts, and each of these parts was then converted into a
method on a class. To make sure that there was no proliferation of
classes, we kept track of all the classes that had been identified
during the process, and tried to reuse previously identified classes as
much as possible. (If I had the time and the mandate to build from
scratch, I would have let the basic object model emerge from a detailed
analysis of the probelm domain)
3) Some of the classes that we identified initially sounded like
controller classes ---- that had "or"s and "er"s in their name, and
they violated the "er-er" principle. For each such class we asked the
question: What does it do, whatever it has to do, to? For example, one
of the classes that we "discovered" was Configurator ----- and we asked
the question: What does the Configurator configure? The answer was the
Execution Environment ---- so we introduced a class named Environment.
4) All the global variables were replaced by the singleton design
pattern ----- for the most part, each global variable became a
singleton instance, but there were instances where a group of global
variables got tied to the same singleton instance.
5) All the API implementations became a single method invocation on an
object ---- any "fanout" to other methods on collaborating objects were
handled in the top level method only. In other words there was no
control logic or "chaining" of object method invocations left in the
API implementation. Even though we did not think of it as a big deal
initially, it did come in pretty handy when we were asked to capture
the new design in the Client's OO tool. On the sequence diagrams we
were able to show the buisness logic of the application as an actor and
the API function call as the interface entity. We were thus able to
clearly delineate between the functional and OO worlds ---- on the
"West" side of the interface entity we had the API function call, and
on the "East" side of the interface entity we had a method invocation
on a class. One of my distignushed colleagues had included some
control logic in the API implementation ---- instead of just a single
method call invocation on an object. During the review, he got
repeatedly thrashed by the question: Which entity provided this
functionality?
All in all I believe that we were able to provide a fairly satisfactory
result (B- in my own estimation) to our client. They seem to be
satisfied, although, truth be told, this is hardly how I would have
proceeded if I had the time and the mandate to start from scratch.
Did I miss anything obvious? Did I do anything blatantly wrong? Did
I overlook anything? Any feedback will be appreciated.
Masood
In this day and age, you never say no to any work that is thrown at you
---- so when I was offered this short-term contract to convert legacy C
code to C++, I did not say no. Personally I believed that it was a
somewhat futile exercise since one of the main requirements was for the
existing API (a functional interface written in C) to remain the same.
I would have much rathered that the mandate be ab-initio, but that was
not the case here. My client had a bad experience with OO, and they
wanted to re-tread this path very very carefully. They were convinced
that a "phased approach" to OO worked much better for them.
I tried to argue (although not very passionately) that functional and
OO design together provide a rather immiscible combination --- one
which nobody is happy with. However, for someone who was prepared to
dig trenches, I did not make a very big deal out of it. Since the API
had to remain the same, and there was not enough time for a detailed
use-case/OO analysis, here's how we got about this task.
1) Studied the current implementation of the APIs --- if it sounded
"coherent", and if it could be easily converted into a method on a
class, we went ahead and did that
2) If the API implementation did bot seem coherent, we chopped it into
coherent parts, and each of these parts was then converted into a
method on a class. To make sure that there was no proliferation of
classes, we kept track of all the classes that had been identified
during the process, and tried to reuse previously identified classes as
much as possible. (If I had the time and the mandate to build from
scratch, I would have let the basic object model emerge from a detailed
analysis of the probelm domain)
3) Some of the classes that we identified initially sounded like
controller classes ---- that had "or"s and "er"s in their name, and
they violated the "er-er" principle. For each such class we asked the
question: What does it do, whatever it has to do, to? For example, one
of the classes that we "discovered" was Configurator ----- and we asked
the question: What does the Configurator configure? The answer was the
Execution Environment ---- so we introduced a class named Environment.
4) All the global variables were replaced by the singleton design
pattern ----- for the most part, each global variable became a
singleton instance, but there were instances where a group of global
variables got tied to the same singleton instance.
5) All the API implementations became a single method invocation on an
object ---- any "fanout" to other methods on collaborating objects were
handled in the top level method only. In other words there was no
control logic or "chaining" of object method invocations left in the
API implementation. Even though we did not think of it as a big deal
initially, it did come in pretty handy when we were asked to capture
the new design in the Client's OO tool. On the sequence diagrams we
were able to show the buisness logic of the application as an actor and
the API function call as the interface entity. We were thus able to
clearly delineate between the functional and OO worlds ---- on the
"West" side of the interface entity we had the API function call, and
on the "East" side of the interface entity we had a method invocation
on a class. One of my distignushed colleagues had included some
control logic in the API implementation ---- instead of just a single
method call invocation on an object. During the review, he got
repeatedly thrashed by the question: Which entity provided this
functionality?
All in all I believe that we were able to provide a fairly satisfactory
result (B- in my own estimation) to our client. They seem to be
satisfied, although, truth be told, this is hardly how I would have
proceeded if I had the time and the mandate to start from scratch.
Did I miss anything obvious? Did I do anything blatantly wrong? Did
I overlook anything? Any feedback will be appreciated.
Masood