Re: Abstract Method w/o Factory

From: H. S. Lahman (h.lahman_at_verizon.net)
Date: 10/18/03


Date: Sat, 18 Oct 2003 18:23:01 GMT

Responding to Stocks...

>
>
>>It is fairly common for a Factory to instantiate both the instance of
>>Client and the relationship. Factories are convenient for that because
>>they isolate the rules and policies of instantiation in one place.
>>(Typically the rules and policies for who participates in a relationship
>>are orthogonal to the rules and policies of collaboration between those
>>participants.)
>>
>
>
> I was making an effort to avoid adding a Factory to the product but
> that effort may be in vain.

It would only be a problem if it had to replace something in the legacy
code that currently instantiates Base. Doing so would, inevitably,
result in some degree of implementation-level surgery in the legacy code
around the replaced capability. OTOH, it will make the legacy more
robust by isolating those responsibilities.

>
>
>>How does GetInstance know which derived class to instantiate? [My C++
>>is rusty but I don't see how GetInstance can be invoked without a
>>specific instance of [Base] in hand (unless it is a static method).]
>>
>
>
> GetInstance would have to be a static method that applies the well
> defined logic (it reads from an EEPROM) to determine if the client
> needs A or B.

OK, if I understand this right, in the legacy code a Base was just
created by somebody because there weren't any choices, with something like:

OldSomebody::doIt
     ...
     myBase = new Base (...)
     ...
     myClient = new Client (myBase)
     ...

and you don't want OldSomebody to be reading EEPROMs so you want
something like:

OldSomebody::doIt
    ...
    myBase = <someoneElse>->getInstance()
    ...
    myClient = new Client (myBase)
    ...

If so, then the issue becomes whether <someoneElse> should be Base
itself of some other object like the a Factory.

I would argue that OldSomebody is going to be changed in any case, so it
doesn't matter, from the existing legacy perspective, who owns the new
responsibility of reading EEPROMs. Given that, I would vote for the
Factory approach because I just don't see a justification for that being
a Base responsibility.

The very fact that you have to change it now argues for the volatility
that wants the instantiation responsibilities encapsulated AND isolated
in a cohesive unit. Then next month when the hardware changes so that
some 12-step protocol is required to get all the relevant information,
you can make that Factory into its own state machine and neither Client
nor Base will be aware of it.

>
>
>>I don't care for this solution because I think it is highly unlikely
>>[Base] should be responsible for knowing what sort of leaf class to
>>create. It is extremely rare (not to mention incestuous) for an object
>>to be responsible for instantiating instances of the same class. In
>>most problem spaces someone else (i.e., an object of another class)
>>understands the rules and policies of instantiation. [This is one of
>>the uses for Jacobson's "controller" classes.]
>>
>
>
> This solution doesn't sit well with the OO part of my gut but I was
> trying to avoid adding extra classes to the design. The logic that
> selects A or B can only be used to select A or B, its not reusable. So
> adding a Factory adds a new class and a new dependancy to every client
> that effectively just picks between 2 instances.

IMO, angst over adding extra classes is akin to angst over keystrokes.
It really shouldn't affect the solution. IOW, one identifies the
classes necessary to solve the problem and relies on things like
invariants to make that solution simpler.

For example, an conditionality in a relationship will require code to
test that conditionality and that code will be executed every time a
message is sent via that relationship. So, as a general rule one seeks
to add classes if it can eliminate conditionality. Thus a basic linked
list might be represented as:

              0..1 starts at 1 0..1 precedes
[LinkedList] ----------------- [Element] --------------+
                                   | 0..1 |
                                   | follows |
                                   | |
                                   +--------------------+

However, one can eliminate the conditionality via:

             precedes 1 1 precedes
        +-------------- [Element] ------------------+
        | A |
        | | |
        | +-----------+-----------+ |
        | | | | |
        +---- [Start] [End] [Intermediate] --+
    follows 1 | 1 1 follows
                  | starts at
                  |
                  | 1
            [LinkedList]

One has added three subclasses whose only difference lies in the number
of referential attributes they have for navigation. This diagram is not
as elegant but the code will be both faster and more robust because the
rules for a linked list have been built into the relationships rather
than the code. That is, no matter how the list is navigated the rules
are enforced only in the place where the list is instantiated.

[I would argue that it is also clearer about what is going on. Because
the first is essentially an idiom for representation, it is pretty clear
for all the baggage that comes with the notion of a linked list.
However, that will not necessarily be the case for a structure in the
customer problem space.

Also, the context has assumptions. If there can be a list with only one
element, then the [Element] end of the Start/Element relationship is
conditional. One can eliminate that conditionality as well, at the cost
of more classes. Eventually one has to make a tradeoff between
complexity in the Class Diagram vs. complexity in the code code. (Leon
Starr has a good discussion of this in "Executable UML: How to Build
CLass Models".)]

As far as reuse is concerned, use of a Factory is really about
consistency and encapsulation. The rules and policies for instantiation
are usually different than the rules and policies for collaboration.
Therefore it makes sense to encapsulate them. It also makes sense, from
an OO viewpoint, to keep classes cohesive (i.e., limit their
responsibilities). So the Factory represents a pattern for complexity
management that captures both ideas. In doing so, it provides
consistency that allows one to capitalize on design reuse.

*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
hsl@pathfindermda.com
Pathfinder Solutions -- Put MDA to Work
http://www.pathfindersol.com
(888)-OOA-PATH



Relevant Pages

  • Re: implementing roles in OOP......
    ... relationship instantiation from relationship navigation. ... every Element subclass. ... The Client provides the Visitor reference when it invokes ... >>encapsulated as knowledge responsibilities in a single class. ...
    (comp.object)
  • Re: Abstract public member variales?
    ... The relationship is instantiated in the factory and remains ... Serializable.saveItwhen using the Serializer pattern. ... Context upon any one particular algorithm of a class of related ... responsibilities of Context; ...
    (comp.object)
  • Re: why a factory class?
    ... by allowing Factory pattern strategy it allows better encapsulation ... > instantiation context. ... > problems so those patterns are fairly elaborate solutions. ...
    (comp.object)
  • Re: OO data model for scheduling program
    ... You are making an assumption that inclusion polymorphism (substitution ... relationship instantiation based upon external configuration data. ... depreciation and a given asset's depreciation can be computed by one of ... Then assign those responsibilities to classes individually based on ...
    (comp.object)
  • Re: why a factory class?
    ... > separate factory object to create many similar objects, ... Then when those rules and policies change, ... navigation context. ... instantiation context. ...
    (comp.object)