Google
 
Webnews.only-4-geeks.com
Interesting places
news.only-4-geeks.com Forum Index » ObjectGoto page 1, 2  Next

design problem

 
Jump to:  
 
Nick Keighley
PostPosted: Wed Jun 04, 2008 6:58 am    Post subject: design problem
       
Hi,

This is a rather "thinking out loud" post as I suspect if I could ask
a clear question I'd be able
to ask it myself.

Suppose the system handles Calls between Parties. Calls process
Messages that arrive from some
comms infrastructure (say Channels). So a Channel creates a Messages
when the socket or whatever
signals that there is data to process. Messages fall into two
catagories those that belong to existing
Calls and those that create new Calls (I'm ignoring errors at the
moment).

So my first thought was a CallManager. (code is Not Quite C++)

CallManager::process_message (Message& msg)
{
Call* call;

if (is_new_call(msg))
call = create_call (msg);
else
call = lookup_call (msg.call_id());

call->process_msg (msg);
}

It's the name that put me off. CallManager looks a bit GodClass like.
I could change the name
to MessageDispatcher, but that is only a name change. Or make the
Channel class create
and lookup calls (that sounds deeply wrong). Or should the message
process itself?

class Message
{
public:
virtual received_action() = 0;
};

class NewCallMessage: public Message
{
public:
received_action();
}

similarly ExistingCallMessage

NewCallMessage::received_action()
{
call = create_call();
}


no... that isn't going to work.

Ok, try this

class Message
{
public:
virtual Call* get_call() = 0;
private:
received_action()
{
Call* call = get_call(); // call needs to be stored
somewhere...
call->process_call();
}
};

Call* NewCallMessage::get_call()
{
return create_call (*this);
}

Call* ExistingCallMessage::get_call()
{
return lookup_call (*this);
}

I think I need some sort of CurrentCallCollection. New calls will be
added to it
existing calls will be looked up in it and dead calls removed.

So is the CallManager a good idea or should calls "process
themselves">



--
Nick Keighley
 

 
S Perryman
PostPosted: Wed Jun 04, 2008 6:58 am    Post subject: Re: design problem
       
Nick Keighley wrote:

Quote:
This is a rather "thinking out loud" post as I suspect if I could ask
a clear question I'd be able to ask it myself.
Suppose the system handles Calls between Parties. Calls process
Messages that arrive from some
comms infrastructure (say Channels). So a Channel creates a Messages
when the socket or whatever
signals that there is data to process. Messages fall into two
catagories those that belong to existing
Calls and those that create new Calls (I'm ignoring errors at the
moment).

[ musings snipped - but read and acknowledged ]

Quote:
So is the CallManager a good idea or should calls "process
themselves" .

Communications systems have a canonical model for services :

The service (telephony, Internet pipe etc) .

The user (U) plane, which provides the resources needed to support
the service (connections, codecs etc) .

The control (C) plane, which provides the means to make service
requests, and configures the U-plane in order to satisfy the request.


Your "CallManager" is the C-plane component.
In the comms world, the C-plane *is* God.

The "God class" problem is effectively one of s/w granularity.
You have a C-plane function, but there is nothing inherent in the
subject domain that demands the implementation of the function as
one distinct component (a class etc) .

A common OO implementation is that each service becomes an object
in its own right, encapsulating the "work flow" for each service
request. The C-plane service receiver becomes a factory that
creates an appropriate service object for the C-plane message
received. The service object is then invoked and the request is
processed.


Regards,
Steven Perryman
 

 
Daniel T.
PostPosted: Wed Jun 04, 2008 9:37 am    Post subject: Re: design problem
       
Nick Keighley <nick_keighley_nospam@hotmail.com> wrote:

Quote:
This is a rather "thinking out loud" post as I suspect if I could ask
a clear question I'd be able
to ask it myself.

Suppose the system handles Calls between Parties. Calls process
Messages that arrive from some
comms infrastructure (say Channels). So a Channel creates a Messages
when the socket or whatever
signals that there is data to process. Messages fall into two
catagories those that belong to existing
Calls and those that create new Calls (I'm ignoring errors at the
moment).

So my first thought was a CallManager. (code is Not Quite C++)

CallManager::process_message (Message& msg)
{
Call* call;

if (is_new_call(msg))
call = create_call (msg);
else
call = lookup_call (msg.call_id());

call->process_msg (msg);
}

Note: the above isn't much different than the FlyweightFactory defined
in GoF (in the Flyweight pattern.)

if (flyweight[key] exists) {
return existing flyweight;
} else {
create new flyweight;
add it to pool of flyweights;
return the new flyweight;
}

What you have above sounds like a relatively standard factory pattern to
me. Factories aren't gods... :-)

That said, if an object is constantly having to get the call from this
factory then maybe it should simply hold on to it after the first lookup
instead.
 

 
Nick Keighley
PostPosted: Wed Jun 04, 2008 10:13 am    Post subject: Re: design problem
       
On Jun 4, 9:39 am, S Perryman <q...@q.com> wrote:
Quote:
Nick Keighley wrote:

This is a rather "thinking out loud" post as I suspect if I could ask
a clear question I'd be able to ask it myself.
Suppose the system handles Calls between Parties. Calls process
Messages that arrive from some
comms infrastructure (say Channels). So a Channel creates a Messages
when the socket or whatever
signals that there is data to process. Messages fall into two
catagories those that belong to existing
Calls and those that create new Calls (I'm ignoring errors at the
moment).

So is the CallManager a good idea or should calls "process
themselves"  .

Communications systems have a canonical model for services :

The service (telephony, Internet pipe etc) .

The user (U) plane, which provides the resources needed to support
the service (connections, codecs etc) .

The control (C) plane, which provides the means to make service
requests, and configures the U-plane in order to satisfy the request.

Your "CallManager" is the C-plane component.
In the comms world, the C-plane *is* God.

The "God class" problem is effectively one of s/w granularity.
You have a C-plane function, but there is nothing inherent in the
subject domain that demands the implementation of the function as
one distinct component (a class etc) .

A common OO implementation is that each service becomes an object
in its own right, encapsulating the "work flow" for each service
request. The C-plane service receiver becomes a factory that
creates an appropriate service object for the C-plane message
received. The service object is then invoked and the request is
processed.

ok, thanks!

--
Nick Keighley
 

 
H. S. Lahman
PostPosted: Wed Jun 04, 2008 3:05 pm    Post subject: Re: design problem
       
Responding to Keighley...

Quote:
Suppose the system handles Calls between Parties. Calls process
Messages that arrive from some
comms infrastructure (say Channels). So a Channel creates a Messages
when the socket or whatever
signals that there is data to process. Messages fall into two
catagories those that belong to existing
Calls and those that create new Calls (I'm ignoring errors at the
moment).

So we have something like:

0..1
[Party] ----------+
| 0..1 |
| talks to | R1
| |
+--------------+
|
|
[Call]
| 1
|
| R2
|
| processes
| *
[Message]
| *
|
| R3
|
| created by
| 1
[Channel]
| *
| triggers
|
| R4
|
| 1
[Socket]

Allocating responsibilities here for processing calls and messages is
fairly straight forward. So you don't need a Call Manager to do that.

As I understand your concern the issue is with who instantiates all
these objects and relationships. I assume [Party], [Socket], and
[Channel] are handled specially outside the scope of message processing
and we don't need to worry about that. That leaves [Call] and [message].

[Message] seems like something [Channel] should create since [Channel]
is a surrogate for external handling of messages. So the tricky part
devolves down to instantiating the R2 association when a Message is
created. That, in turn, may require that a Call be created and the R1
association be instantiated.

One possibility is to let [Channel] do all that. The rationale is that
it already is essentially a factory object by nature of its relationship
with [Message]. When one instantiates things, ideally one wants to keep
all the relevant rules and policies under a single scope to make it
easier to ensure referential integrity. IOW, when one instantiates
objects with unconditional associations to other objects, referential
integrity demands that the other objects be created at the same time
(relative to the solution flow of control).

However, cohesion is another concern. The reason one employs factory
objects is to encapsulate those rules and policies because they are
usually different than the rules and policies that govern
collaborations. So one wants one's factory objects to be dedicated to
just that subject matter, especially if those rules and policies are
somewhat complicated as they seem to be here.

Here [Channel] may have some other responsibilities related to things
like converting formats from the [Channel] into a more useful form for
message processing. If those are at all complicated, one might want to
encapsulate them separately from the instantiation rules and policies.
In that case, [Channel] has the responsibility for converting formats
(or whatever) while some other object would act as factory to create
[Message] and, if necessary, [Call]. That can easily be accommodated by
modifying the bottom part of the example diagram:

* creates
[Message] ------------+ R5
| * |
| formats for |
| | 1
| R3 [MessageFactory]
| | 1
| | invokes
| 1 1 |
[Channel] ------------+ R6
| *
| triggers
|
| R4
|
| 1
[Socket]

Now [MessageFactory] is essentially your [CallManager]; it just has a
name with less emotional baggage and it is dedicated exclusively to
instantiation. To do that it may need to collaborate with [Channel] to
get the proper formating (or whatever), which it would pass through to
[Message] attributes.

Note that the R3 association really isn't necessary any more since
[MessageFactory] is a middleman. The formatting collaboration is via R6
during the initialization of [Message] by [MessageFactory]. This segues
to an interesting point: how does the Message actually get processed?
Presumably it would be processed as soon as it is instantiated and the
relevant associations have been instantiated. [MessageFactory] knows
when that is complete, so it is the logical one to send the kick-off
message to Call to announce that a Message is available for processing.
Thus the flow of control is just daisy-chained through the
[MessageFactory] middleman and all we have done is separate and
encapsulate the concerns of instantiation from those of extracting
initialization data from [Channel].



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

H. S. Lahman
hsl@pathfindermda.com
Pathfinder Solutions
LINK
blog: LINK
"Model-Based Translation: The Next Step in Agile Development". Email
info@pathfindermda.com for your copy.
Pathfinder is hiring:
LINK
(888)OOA-PATH
 

 
Nick Keighley
PostPosted: Thu Jun 05, 2008 7:59 am    Post subject: Re: design problem
       
On 4 Jun, 12:37, "Daniel T." <danie...@earthlink.net> wrote:
Quote:
Nick Keighley <nick_keighley_nos...@hotmail.com> wrote:
This is a rather "thinking out loud" post as I suspect if I could ask
a clear question I'd be able
to ask it myself.

Suppose the system handles Calls between Parties. Calls process
Messages that arrive from some
comms infrastructure (say Channels). So a Channel creates a Messages
when the socket or whatever
signals that there is data to process. Messages fall into two
catagories those that belong to existing
Calls and those that create new Calls (I'm ignoring errors at the
moment).

So my first thought was a CallManager. (code is Not Quite C++)

CallManager::process_message (Message& msg)
{
     Call* call;

     if (is_new_call(msg))
           call = create_call (msg);
     else
           call = lookup_call (msg.call_id());

     call->process_msg (msg);
}

Note: the above isn't much different than the FlyweightFactory defined
in GoF (in the Flyweight pattern.)

   if (flyweight[key] exists) {
      return existing flyweight;
   } else {
      create new flyweight;
      add it to pool of flyweights;
      return the new flyweight;
   }

What you have above sounds like a relatively standard factory pattern to
me. Factories aren't gods... Smile

well it seemed a little more than a factory to me in that the
[Message]
has to be dispatched to the appropriate [Call]


Quote:
That said, if an object is constantly having to get the call from this
factory then maybe it should simply hold on to it after the first lookup
instead

yes , but the "hanging on to" has to involve keeping it somewhere.
In your code above the pool of flyweights. I was assuming CallManager
had a collection-of-active-calls or some such. There may be many
calls
and the messages to different calls can interleave arbitarily.


--
Nick Keighley
 

 
Daniel T.
PostPosted: Thu Jun 05, 2008 11:19 pm    Post subject: Re: design problem
       
In article
Nick Keighley <nick_keighley_nospam@hotmail.com> wrote:
Quote:
"Daniel T." <danie...@earthlink.net> wrote:
Nick Keighley <nick_keighley_nos...@hotmail.com> wrote:

CallManager::process_message (Message& msg)
{
     Call* call;

     if (is_new_call(msg))
           call = create_call (msg);
     else
           call = lookup_call (msg.call_id());

     call->process_msg (msg);
}

Note: the above isn't much different than the FlyweightFactory
defined in GoF (in the Flyweight pattern.)

   if (flyweight[key] exists) {
      return existing flyweight;
   } else {
      create new flyweight;
      add it to pool of flyweights;
      return the new flyweight;
   }

What you have above sounds like a relatively standard factory
pattern to me. Factories aren't gods... :-)

well it seemed a little more than a factory to me in that the
[Message] has to be dispatched to the appropriate [Call]

Why is that the case? What if we did this?

Call* CallManager::getCall(/*whatever msg.call_id() returns*/)
{
Call* result = 0;
if (is_new_call(id))
result = lookup_call(id);
else
result = create_call(id);
return result;
}

The client would then do:

callManagaer.getCall(msg.call_id())->process(msg);

Now the callManager is nothing more than a container that holds calls.
It no longer associates with "Message", it doesn't even need to
associate with Call! In C++, a std::map will do. No one could call it a
god then. :-)

Quote:
That said, if an object is constantly having to get the call from
this factory then maybe it should simply hold on to it after the
first lookup instead

yes, but the "hanging on to" has to involve keeping it somewhere.
In your code above the pool of flyweights.

No, that wasn't my point. In order for your code to make sense, (at
least to me,) it must be the case that there is something in a message
that identifies what call should use it. Why not make that association
explicit in the code?
 

 
Nick Keighley
PostPosted: Fri Jun 06, 2008 9:23 am    Post subject: Re: design problem
       
On 6 Jun, 02:19, "Daniel T." <danie...@earthlink.net> wrote:
Quote:
In article
Nick Keighley <nick_keighley_nos...@hotmail.com> wrote:
"Daniel T." <danie...@earthlink.net> wrote:
Nick Keighley <nick_keighley_nos...@hotmail.com> wrote:

I'm probably making mountains out of molehills...


Quote:
CallManager::process_message (Message& msg)
{
     Call* call;

     if (is_new_call(msg))
           call = create_call (msg);
     else
           call = lookup_call (msg.call_id());

     call->process_msg (msg);
}

Note: the above isn't much different than the FlyweightFactory
defined in GoF (in the Flyweight pattern.)

   if (flyweight[key] exists) {
      return existing flyweight;
   } else {
      create new flyweight;
      add it to pool of flyweights;
      return the new flyweight;
   }

What you have above sounds like a relatively standard factory
pattern to me. Factories aren't gods... :-)

well it seemed a little more than a factory to me in that the
[Message] has to be dispatched to the appropriate [Call]

Why is that the case?

well *someone* has to associate [Messages] with [Calls]


Quote:
What if we did this?

Call* CallManager::getCall(/*whatever msg.call_id() returns*/)

call_id is some unique identifier assigned to the call when
it's created. It probably just an integer.


Quote:
{
   Call* result = 0;
   if (is_new_call(id))
      result = lookup_call(id);
   else
      result = create_call(id);
   return result;

}

The client would then do:

   callManagaer.getCall(msg.call_id())->process(msg);

yes but. "who is the client" is the question I'm trying to ask!


class Client
{
msg_received(Msg&);
};

// called by Channel
Client::msg_received (Msg& msg)
{
callManagaer.getCall(msg.call_id())->process(msg);
}

now [Client] *could* be [Channel], but that seems to be loading too
much on [Channel]. I'd been trying to make [Client] be [CallManager].


Quote:
Now the callManager is nothing more than a container that holds calls.
It no longer associates with "Message", it doesn't even need to
associate with Call!

well it contains [Calls]


Quote:
In C++, a std::map will do. No one could call it a
god then. :-)

That said, if an object is constantly having to get the call from
this factory then maybe it should simply hold on to it after the
first lookup instead

yes, but the "hanging on to" has to involve keeping it somewhere.
In your code above[,] the pool of flyweights.

No, that wasn't my point. In order for your code to make sense, (at
least to me,) it must be the case that there is something in a message
that identifies what call should use it.

that's the call_id thing

Quote:
Why not make that association
explicit in the code

how? Somehow there's a mapping from call_id to [Call]
and I agree a map seems like a good way to do this.

I'm beginning toi think the only god-like about my class
was the name.

--
Nick Keighley
 

 
Daniel T.
PostPosted: Fri Jun 06, 2008 7:24 pm    Post subject: Re: design problem
       
On Jun 6, 5:23 am, Nick Keighley <nick_keighley_nos...@hotmail.com>
wrote:
Quote:
On 6 Jun, 02:19, "Daniel T." <danie...@earthlink.net> wrote:

In article
Nick Keighley <nick_keighley_nos...@hotmail.com> wrote:
"Daniel T." <danie...@earthlink.net> wrote:
Nick Keighley <nick_keighley_nos...@hotmail.com> wrote:

I'm probably making mountains out of molehills...





CallManager::process_message (Message& msg)
{
     Call* call;

     if (is_new_call(msg))
           call = create_call (msg);
     else
           call = lookup_call (msg.call_id());

     call->process_msg (msg);
}

Note: the above isn't much different than the FlyweightFactory
defined in GoF (in the Flyweight pattern.)

   if (flyweight[key] exists) {
      return existing flyweight;
   } else {
      create new flyweight;
      add it to pool of flyweights;
      return the new flyweight;
   }

What you have above sounds like a relatively standard factory
pattern to me. Factories aren't gods... :-)

well it seemed a little more than a factory to me in that the
[Message] has to be dispatched to the appropriate [Call]

Why is that the case?

well *someone* has to associate [Messages] with [Calls]

What if we did this?

Call* CallManager::getCall(/*whatever msg.call_id() returns*/)

call_id is some unique identifier assigned to the call when
it's created. It probably just an integer.

{
   Call* result = 0;
   if (is_new_call(id))
      result = lookup_call(id);
   else
      result = create_call(id);
   return result;

}

The client would then do:

   callManagaer.getCall(msg.call_id())->process(msg);

yes but. "who is the client" is the question I'm trying to ask!

How long does a Message object live? Is it just a data packet that
needs to be passed to some Call object processed and then dumped?

Quote:
class Client
{
       msg_received(Msg&);

};

// called by Channel
Client::msg_received (Msg& msg)
{
      callManagaer.getCall(msg.call_id())->process(msg);

}

now [Client] *could* be [Channel], but that seems to be loading too
much on [Channel]. I'd been trying to make [Client] be [CallManager].

Not at all. Channel makes Message objects right? And Message objects
know what Call they should be associated with.

What would happen if you put each Call on its own thread? Then the
Channel would just pass the message to the correct Call and let Calls
do what they will. Even if each Call isn't on a seperate thread,
that's what Channel should be doing. Is there more than one Channel?
If so, can messages bound to one Call come in from different channels?
 

 
Nick Keighley
PostPosted: Mon Jun 09, 2008 3:43 pm    Post subject: Re: design problem
       
On 6 Jun, 20:24, "Daniel T." <danie...@earthlink.net> wrote:
Quote:
On Jun 6, 5:23 am, Nick Keighley <nick_keighley_nos...@hotmail.com
wrote:
On 6 Jun, 02:19, "Daniel T." <danie...@earthlink.net> wrote:
In article
Nick Keighley <nick_keighley_nos...@hotmail.com> wrote:
"Daniel T." <danie...@earthlink.net> wrote:
Nick Keighley <nick_keighley_nos...@hotmail.com> wrote:

I'm probably making mountains out of molehills...

CallManager::process_message (Message& msg)
{
     Call* call;

     if (is_new_call(msg))
           call = create_call (msg);
     else
           call = lookup_call (msg.call_id());

     call->process_msg (msg);
}

Note: the above isn't much different than the FlyweightFactory
defined in GoF (in the Flyweight pattern.)

   if (flyweight[key] exists) {
      return existing flyweight;
   } else {
      create new flyweight;
      add it to pool of flyweights;
      return the new flyweight;
   }

What you have above sounds like a relatively standard factory
pattern to me. Factories aren't gods... :-)

well it seemed a little more than a factory to me in that the
[Message] has to be dispatched to the appropriate [Call]

Why is that the case?

well *someone* has to associate [Messages] with [Calls]

What if we did this?

Call* CallManager::getCall(/*whatever msg.call_id() returns*/)

call_id is some unique identifier assigned to the call when
it's created. It probably just an integer.

{
   Call* result = 0;
   if (is_new_call(id))
      result = lookup_call(id);
   else
      result = create_call(id);
   return result;

}

The client would then do:

   callManagaer.getCall(msg.call_id())->process(msg);

yes but. "who is the client" is the question I'm trying to ask!

How long does a Message object live? Is it just a data packet that
needs to be passed to some Call object processed and then dumped?

class Client
{
       msg_received(Msg&);

};

// called by Channel
Client::msg_received (Msg& msg)
{
      callManagaer.getCall(msg.call_id())->process(msg);

}

now [Client] *could* be [Channel], but that seems to be loading too
much on [Channel]. I'd been trying to make [Client] be [CallManager].

Not at all. Channel makes Message objects right?

yes


Quote:
And Message objects
know what Call they should be associated with.

usually. NewCall messages also cause the call to be created.


Quote:
What would happen if you put each Call on its own thread?

I don't see a problem with that

Quote:
Then the
Channel would just pass the message to the correct Call and let Calls
do what they will.

ok... but that means Channels will also create calls

Quote:
Even if each Call isn't on a seperate thread,
that's what Channel should be doing. Is there more than one Channel?

yes

Quote:
If so, can messages bound to one Call come in from different channels?-

yes

--
Nick Keighley
 

Page 1 of 2 .:. Goto page 1, 2  Next

Google
 
Webnews.only-4-geeks.com

Windows Update | C++ | C | PHP | JavaScript | Photoshop | Programming | Windows 2000 | Python | Windows XP | Object | Flash | Flash - ActionScript | Paint Shop Pro | Excel | PowerPoint | Access | Word | Windows 98 | Internet Explorer 6.0 | CorelDraw12 | Java | XML | asm x86 | Linux Mandrake | Linux RedHat | Outlook |  | news from newsgroups |_ | s

Web Templates

Awesome Website Templates ©

texas holdem poligrafia egipt prezenty kupno mieszkania