Re: Design help...an explosion of interfaces......

From: H. S. Lahman (h.lahman_at_verizon.net)
Date: 08/29/04

  • Next message: Shayne Wissler: "Re: [NEW!!] Binding together Properties of Objects"
    Date: Sun, 29 Aug 2004 15:54:02 GMT
    
    

    Responding to Daniel T....

    >>>I'm not sure I would use an isBuffered attribute unless a Pipe was able
    >>>to change from buffered to unbuffered and/or back. IE using an attribute
    >>>this way implies that the attribute won't allways be the same for a
    >>>particular object. (for example, Rectangle::isSquare may return true or
    >>>false depending on the state of that particular rectangle.)
    >>
    >>I don't see a problem. There is no rule that requires knowledge
    >>responsibilities to be volatile. On the contrary, when one does
    >>parametric polymorphism the specification objects usually have fixed
    >>values throughout their lives and the variability lies in dynamic
    >>instantiations of their relationships.
    >
    >
    > I'm not saying there is a problem, its just not my preferred way of
    > working. I don't see much difference between:
    >
    > void foo( IPipe& p ) {
    > if ( p.isBuffered() )
    > // do buffered things
    > else
    > // do unbuffered things
    > }
    >
    > and
    >
    > void foo( IPipe& p ) {
    > if ( dynamic_cast<IBufferedPipe*>( &p ) )
    > // do buffered things
    > else
    > // do unbuffered things
    > }

    I didn't read this as Nicholls' problem. Foo doesn't care whether the
    pipe is buffered are not; it is just loading or extracting from the pipe
    and it couldn't care less how Pipe does its thing. IOW, foo does Pipe
    things, not buffered pipe things or unbuffered things. It is the
    overall problem solution context that demands that the client only
    collaborate with a buffered or unbuffered specialization.

              * R1 1
    [Client] ------------------ [Pipe]
    + foo A
                                   |
                           +-------+-------+
                           | |
                       [Buffered] [Unbuffered]

    If one tries to enforce type safety doing that one has to invoke

    void foo (IBufferedPipe p)
    {
         p.load(...)
    }

    but Nicholls' problem is that another Client may need to collaborate
    with an unbuffered pipe. So one needs a different method:

    void fooPrime (IUnbufferedPipe p)
    (
         p.load(...)
    }

    or some kind of overloading, which is what I believe prompted the OP.
    In addition, the client of Client.foo needs to understand the context
    and call the right method with the right Pipe subclass instance. That's
    because Client's client is also instantiating the relationship in
    addition to collaborating with Client.

    So my answer is not to pass a pipe to foo at all. Let some third party
    who understands the buffering context instantiate the relationship and
    then one has:

    void foo ()
    {
        myPipe.load(...)
    }

    It would only be that third party that had to somehow make sure only
    buffered or unbuffered pipes got into the relationship.

    That might yield a factory that looked like:

    void ClientFactory::factory ()
    {
         Pipe p
         Client c = new Client
         ...
         p = myPipeList.getNext()
         if (p.isBuffered)
         {
             c.setR1 (p)
             return
         }
    }

    [Note that if the factory is creating Pipe instances, one doesn't need
    any checking at all because the subclass instance will be known when
    invoking new.]

    In this simple example checking isBuffered is semantically equivalent to
    checking the subclass by a downcast because the results are the same.
    However, I argue it is not semantically the same thing. What defines
    Buffered pipe is the nature of the behavior (i.e., its implied DbC
    contract for that responsibility). However, when checking the property
    we are only checking whether the instance in hand has the behavior, not
    its nature nor anything about other properties that may define the
    specialization. That distinction becomes clear when one deals with
    multiple characteristics:

                          [Pipe]
                          + type
                            A
                            |
                 +----------+----------+
                 | |
             [Buffered] [Unbuffered]
                 A A
                 | |
          +------+----+ +-----+-------+
          | | | |
    [Character] [Binary] [Character] [Binary]

    We still have a single type variable but it has a data domain of
    {BufferedAndCharacter, BufferedNotCharacter, NotBufferedAndCharacter,
    NotBufferedNotCharacter}. That also exactly maps to the leaf subclass
    but it is clearly not the same as testing the class via downcasting
    (i.e., one doesn't need to "walk" the tree itself with the downcasts).

    One can construct other solutions for the same semantics:

                          [Pipe]
                          + isCharacter
                          + type
                            A
                            |
                 +----------+----------+
                 | |
             [Buffered] [Unbuffered]

    Now type attribute has the same data domain but it is clearly
    semantically different than simply mapping the subclass. (There are 1NF
    reasons for not doing this, but that is orthogonal to the subclassing
    issue.)

    Bottom line: the type attribute has a different semantics than the class
    type. One can abuse that by deliberately mapping it to the subclass and
    then using it to navigate a subclassing relationship just like a
    dynamic_cast. But in that case one has failed to properly instantiate
    the direct subclass relationship so a good reviewer will jump on that as
    quickly as on using a dynamic_cast.

    Note that this ties back to my issue of the problem being solved above.
      I would be using the isBuffered check to /instantiate/ the
    relationship, not navigate it. One of the benefits of not passing
    object references around is that it makes it clear when one is
    instantiating vs. navigating relationships. As I pointed out to
    Nicholls, one can view isBuffered as part of the implementation of the
    direct subclass relationship just as one might need a static find(id)
    method to locate specific instances when instantiating a relationship.

    *************
    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.pathfindermda.com
    blog (under constr): http://pathfinderpeople.blogs.com/hslahman
    (888)-OOA-PATH


  • Next message: Shayne Wissler: "Re: [NEW!!] Binding together Properties of Objects"

    Relevant Pages

    • Re: Design help...an explosion of interfaces......
      ... >>comment on any subclass access problem. ... > Its pretty clear that some transactions between client code and Pipe ... one has logical encapsulation of the context ...
      (comp.object)
    • Re: Design help...an explosion of interfaces......
      ... >> I have a set of class libraries that implement a pipe, pipereader, ... > very broad contexts, but one still needs to understand the context to ... > comment on any subclass access problem. ... implementations get cluttered with things like.... ...
      (comp.object)
    • Re: Design help...an explosion of interfaces......
      ... > pipe implementations have the same interface signatures their ... I agree with Daniel T. et al that the context needs to be explained ... comment on any subclass access problem. ... If your client cares whether the pipe is buffered or not, ...
      (comp.object)
    • Re: stdout redirected to a processs own stdin
      ... Probably does not put anything in the pipe because output is buffered. ... > I suspect this is due to a misunderstanding of how pipes work. ... The UNIX system has no way of knowing about any ... turn off the output buffering with setbuf or flush the ...
      (comp.unix.programmer)
    • SUMMARY: Tru64 pipe buffer size
      ... The problem was that the application detected whether its stdout was going to file (pipe) or term, and did buffering in the case of a file. ... there was an easier way around the problem in our particular situation: Pipe the output via the tee command. ... Subject: Tru64 pipe buffer size ... We have a third-party tool which writes line-by-line to terminal device, but when its output is piped it seems to be buffering the data in chunks of approx 4KB at a guess. ...
      (Tru64-UNIX-Managers)