Click here to Skip to main content
15,885,985 members
Articles / Desktop Programming / WTL
Article

A Document-View foundation for WTL. Part 1

Rate me:
Please Sign up or sign in to vote.
3.50/5 (2 votes)
9 Mar 2000 85.1K   1.6K   29   5
A library that provides the easiest way to get loosely coupled components.
  • Download demo project - 157 Kb
  • Download source files - 7 Kb

    Introduction

    I don't know how about you but I was literally pumped when I learned about WTL. I'm not about to suggest that people should just dump MFC, everybody has its own considerations when it comes to choosing a tool. What's really important it became easier to do without it.

    As some of you might know by now one of the missing things in WTL is document-view architecture, also known as DVA. Now, quite a few people have criticized DVA. And while I don't find it particularly flexible or even generic enough to play such an important role in MFC, that should not undermine the idea of separating the visual representation from the rest of the logic. So I figured I'll take my shot at it. Only mine of course will be different. Here is how:

    First and most important we'll start with something even more generic than separating the UI from the application. Then we'll build that separation on top of it.

    The idea isn't new, it has been known under different names for some time now - publish-subscribe, event sources and event listeners, connection points, delegates... All of those are forms of the same programming paradigm: Object A does something and object B reacts on it. You could say there is a connection between them two. Which brings us to the name - Connectable Objects. Ok, the name isn't mine either. The name, as well as the model was pretty much borrowed from COM. And since that's the case, I figured I'm not going to confuse people with newly invented terms, and will stick with familiar ones. So we have connection points, sink objects, the whole crew. Here is a link to a more detailed description of the concept.

    We'll start with explaining what were the priorities when designing the library:

    • Strong typing
      That means no more eventCode, notificationCode, LPARAM and so on, just straight calls.
    • Minimal, impact on your existing design
      Tried to keep it down with "to inherit a bananas get the whole gorilla" approach.
    • Ease of use and understanding
      The goal here is to make it so seamless as it almost was part of the language. (I sure wish it was as it is one of my favorite techniques.)
    • Have it efficient. If an object has to notify somebody about its event we don't want this to slow it down significantly.
    • It's also more of a ATL-like code taking advantage of things like RTTI and multiple inheritance, the lack or the avoidance of which, in my opinion, constrained at the time the design of MFC. It's not really important but I thought I'd mention it.

    What we don't have is thread marshaling, so if your sink object is observing objects running in different threads you'll have to take care of it yourself.

    Sorry, that's the price we had to pay for strong typing and efficiency. You can however implement your own marshaling when you need it, this way you don't bloat the majority of applications that simply don't use multiple threads.

    I'm also planning to look into a generic solution for this.


    A sample code

    The lecture is getting a little boring so let's dig right in and examine some code.

    How do you get two objects connected anyway?

    We need three classes:

    1. The subject (the object firing the events, the event source)
    2. The observer (or sink object, the object which gets notified about events the first object fires)
    3. The sink interface (the interface observers must implement in order to allow the subject to talk to them)

    That's what it will look like:

    class CSinkInterface {
         virtual void onEvent1(int p1, LPCTSTR p2) = 0;
         .... // other events
    };
     
    class CObserver : public CSinkInterface {
         COSinkOwner m_sinkOwner;
         virtual void onEvent1(int p1, LPCTSTR p2) {
              ... // react somehow
         }
    };
     
    class CSubject : public COConnectionPointContainer {
         ...
         CO_BEGIN_CONNECTIONPOINTS(CSubject)
              CO_CONNECTIONPOINT(CSinkInterface)
         CO_END_CONNECTIONPOINTS()
         void method1()
         {
              ...
              CO_LOOP(CSinkInterface)
                  eventSink->onEvent1(p1, p2);
              CO_ENDLOOP
         }
    };

    And finally how do we connect them?

    CSubject subject;
    CObserver observer;
    
    COConnectionPointImpl<CSinkInterface> *cp;
    
    if(subject.coFindConnectionPoint(&cp))
        cp->Advise(&observer, &observer.m_sinkOwner);

    I intentionally used unrelated names (CSubject and CObserver) to make a clear separation between application and the library. I also decided to use the prefix "CO" instead of "C" in class names to avoid collisions with COM, and the prefix "co" in overridable methods to avoid collisions with your methods.


    A brief description

    So that's the simplest example. Almost every name is known from COM connection points. The only new term here is COSinkOwner. This is the object responsible for breaking the connection when the sink object goes down. On another end, if the event source dies the connection is broken by an object hidden under CO_CONNECTIONPOINT.

    We could also derive CObserver from COSinkOwner and this way we could get rid of m_sinkOwner. That would also buy us the possibility to override a couple of methods (coOnAdvised and coOnUnadvised to be exact) and get notified when we get connected to another object. By the way, CSubject can overwrite coOnConnection and coOnDisconnection to do the same thing.

    The ability to separate the sink owner and the sink object is very important. For example we can get a form to be a sink owner for its controls. It also can help us with using a technique resembling Java' inner classes to solve name collisions under multiple inheritance.

    Just remember one thing - the sink owner should either die before the sink object does or you should take care of calling Unadvise yourself. Or else... right, Dr' Watson takes over the conversation.

    I think it's needless to mention that one object can expose many connection points, so can it connect to many sources. Just as it is with COM connection points.


    Q&A

    So since we have so many similarities why not just use COM?
    Because not every class is meant to be a COM object and not every sink interface needs to be declared in a type library. I think the idea of connectable objects is very valuable by itself since it can help making components more loosely coupled and hence more reusable.

    What about Document-View?
    There are many things that can be written using connectable objects.

    Since UI separation is the most obvious, the most widely used, part two of this article will focus exclusively on that. I think the topic is important and complex enough to deserve a dedicated article.

    One thing I can probably guess, it's very likely that it will be different from MFC doc-view.

    How to contact me
    If you have any questions or suggestions feel free to ICQ me at 11411966 (just type "co" in the authorization request, since I disabled receiving messages from people which are not in my list), or just e-mail me.

    Updates
    Follow this link.

  • License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here


    Written By
    United Kingdom United Kingdom
    This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

    Comments and Discussions

     
    GeneralPlease go on ... Pin
    S.Prymak5-Oct-04 1:12
    sussS.Prymak5-Oct-04 1:12 
    GeneralThis is extremely useful Pin
    cdr17-Apr-03 10:09
    cdr17-Apr-03 10:09 
    GeneralMultithreading support Pin
    Sandu18-Mar-00 18:33
    Sandu18-Mar-00 18:33 
    GeneralCOM+ Event System Pin
    Martin Bohring15-Mar-00 1:24
    Martin Bohring15-Mar-00 1:24 
    GeneralRe: COM+ Event System Pin
    Sandu Turcan15-Mar-00 16:21
    Sandu Turcan15-Mar-00 16:21 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.