Re: polymorphic design approach
From: Anthony Borla (ajborla_at_bigpond.com)
Date: 12/20/03
- Next message: B. v Ingen Schenau: "Re: how to ignore carriage return in c/c++??"
- Previous message: kazack: "Re: Best book on learning C++?"
- In reply to: ma740988: "polymorphic design approach"
- Next in thread: Anthony Borla: "Re: polymorphic design approach"
- Reply: Anthony Borla: "Re: polymorphic design approach"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Sat, 20 Dec 2003 09:08:08 GMT
"ma740988" <ma740988@pegasus.cc.ucf.edu> wrote in message
news:a5ae824.0312191228.51a3ebb3@posting.google.com...
>
> Bear with me as I move deeper into OO style programming
> from a C world. Consider
>
<SNIP CODE>
>
> Now that was great 'stuff' in our C code (old design) but
> I realize after reading and reading .. that said approach in
> a C++ land - is bad programmign style. So now why undermine
> C++. In essence, I'm using one feature of C++ (switch) to
> 'cheat' another feature of C++ (polymorphism). Just 'use'
> polymorphism. The question. Could I garner some guidance
> on a design approach in a polymorphic sense that'll simplify
> the above?
>
I don't think a 'polymorphic' approach is warranted to this problem because
you are not performing a switch on object type, but on object identity. As
such, you have to 'switch' at some point, whether at a high level
[application object level], or at a lower level. Any benefit polymoprhism
could offer here is, IMO, quite minimal.
>
> An aside: I'd like to start without the use of STL maps,
> etc (dont really want to bite off more than I can chew
> right now). The point being. I suspect an even better solution
> lies in the use of the STL maps or perhaps using a approach
> call 'factory' which i perused in one my new texts.
>
The example code makes use of 'std::vector'. I hardly think you'll
experience any difficulty understanding it.
The sample code solves your 'routing' problem by using a highly simplified
'Chain of Responsibility' pattern. In essence:
* A circular chain of processors is set up; each is in a
sibling relationship with its immediate neighbour
* Each processor has a collection zero or more devices;
each device has a unique identifier
* A message intended for a specific device is sent
to a processor; it will then determine whether:
- The message is intended for it; if so, it passes it
to the device collection for possible matching
- The message is not for it; if so, it passes it on
to its sibling effectively 'handing off ' responsibility
for the message handling
* Once the intended device has been located it fires off
its 'handleMessage' function to, as its name implies,
handle the message !
Some polymorphic behaviour has been included, namely in the overriding of
the 'handleMessage' function for different devices. I believe it [with
suitable modification], pretty well, meets the requirements outlined in your
sample code.
Miscellaneous Code Comments:
* The bulk of the code exists to handle STL container
requirements; a linked list of devices could have been
implemented, simplifying the code
* 'Device' pointers are stored so as to allow polymorphic
behaviour. Storing raw pointers requires great care, and
is also cumbersome as you need to consider ownership
issues carefully [i.e. who owns, therefore gets to delete,
the pointers]
* While the sample code has made extensive use of
iterators, much more elegant code is possible by
using the STL algorithms [e.g. 'for_each', 'transform',
etc]. However, these require a little more setting up -
not shown to keep the code 'simple' [though it *is*
cumbersome as is]
* A much better way of storing pointers is via Smart Pointer
objects [i.e. the pointer is a data member of such objects].
Safer, more elegant and robust code can result. Of course,
it is a little more work to set up :) !
* Forgive the inlined code. What I'd intended as a short post
took a little longer than expected [isn't that *always* the
way, but I keep firing up my editor despite this :)]
Hopefully this code helps deepen your understanding of C++.
Anthony Borla
// Sample Code ------------------------------------------------
#include <cstdlib>
#include <cassert>
#include <iostream>
#include <ostream>
#include <string>
#include <vector>
class Message
{
public:
explicit Message(const std::string& procID, const std::string& devID,
const std::string& payload)
: procID_(procID), devID_(devID), payload_(payload)
{}
std::string getProcID() const { return procID_; }
std::string getDevID() const { return devID_; }
std::string getPayload() const { return payload_; }
private:
std::string procID_, devID_;
std::string payload_;
};
class Device
{
public:
explicit Device(const std::string& id) : id_(id) {}
virtual ~Device() {}
std::string getID() const { return id_; }
// Overrride this to do your 'device stuff'
virtual void handleMessage(const Message& message)
{
std::cout << "Default Device: " << message.getPayload() << std::endl;
}
bool canHandleMessage(const Message& message)
{
return (message.getDevID() == getID()) ? (handleMessage(message), true)
: false;
}
friend bool operator<(const Device& left, const Device& right)
{
return left.id_ < right.id_;
}
friend bool operator==(const Device& left, const Device& right)
{
return left.id_ == right.id_;
}
friend bool operator!=(const Device& left, const Device& right)
{
return left.id_ != right.id_;
}
private:
std::string id_;
};
class D1 : public Device
{
public:
explicit D1(const std::string& id) : Device(id) {}
virtual void handleMessage(const Message& message)
{
std::cout << "D1 Type handling: " << message.getPayload() << std::endl;
}
};
class D2 : public Device
{
public:
explicit D2(const std::string& id) : Device(id) {}
virtual void handleMessage(const Message& message)
{
std::cout << "D2 Type handling: " << message.getPayload() << std::endl;
}
};
class D3 : public Device
{
public:
explicit D3(const std::string& id) : Device(id) {}
virtual void handleMessage(const Message& message)
{
std::cout << "D3 Type handling: " << message.getPayload() << std::endl;
}
};
class Processor
{
typedef std::vector<Device*> Devices;
public:
explicit Processor(const std::string& id)
: id_(id), sib_(NULL)
{}
~Processor() { sib_ = NULL; removeAllDevices(); }
bool isLinked() const { return sib_ != NULL; }
Processor* getSibling() const { return sib_; }
void linkToSibling(Processor* sib)
{
if (isLinked()) return;
assert(this != sib && this != sib->getSibling());
sib_ = sib;
}
std::string getID() const { return id_; }
int getCurrentDevices() const { return dev_.size(); }
void addDevice(Device* dev)
{
dev_.push_back(dev);
}
void removeDevice(std::string id)
{
Devices::iterator i;
for (i = dev_.begin(); i != dev_.end(); ++i)
{
if ((*i)->getID() == id)
{
delete *i; dev_.erase(i); break;
}
}
}
void routeToProcessor(const Message& message)
{
if (getID() == message.getProcID())
routeToDevice(message);
else
getSibling()->routeToProcessor(message);
}
private:
void routeToDevice(const Message& message)
{
Devices::iterator i;
for (i = dev_.begin(); i != dev_.end(); ++i)
if ((*i)->canHandleMessage(message))
break;
}
void removeAllDevices()
{
Devices::iterator i;
for (i = dev_.begin(); i != dev_.end(); ++i)
delete *i;
dev_.clear();
}
private:
std::string id_;
Processor* sib_;
Devices dev_;
};
// Driver
int main()
{
{
Processor p1("p1");
Processor p2("p2");
Processor p3("p3");
Processor p4("p4");
p1.addDevice(new Device("d11"));
p1.addDevice(new Device("d12"));
p1.addDevice(new Device("d13"));
p2.addDevice(new D2("d21"));
p2.addDevice(new D1("d22"));
p2.addDevice(new D3("d23"));
p3.addDevice(new D3("d31"));
p3.addDevice(new D2("d32"));
p3.addDevice(new D3("d33"));
std::cout << p1.getCurrentDevices() << std::endl;
std::cout << p2.getCurrentDevices() << std::endl;
std::cout << p3.getCurrentDevices() << std::endl;
std::cout << p4.getCurrentDevices() << std::endl;
// Setup circular chain of processors: p1 ... p4 ... p1
p1.linkToSibling(&p2);
p2.linkToSibling(&p3);
p3.linkToSibling(&p4);
p4.linkToSibling(&p1);
p1.routeToProcessor(Message("p1", "d13", "abc"));
p1.routeToProcessor(Message("p2", "d21", "123"));
p1.routeToProcessor(Message("p3", "d33", "DEF"));
p4.routeToProcessor(Message("p1", "d14", "abc"));
p4.routeToProcessor(Message("p2", "d22", "123"));
p4.routeToProcessor(Message("p3", "d31", "DEF"));
}
return EXIT_SUCCESS;
}
- Next message: B. v Ingen Schenau: "Re: how to ignore carriage return in c/c++??"
- Previous message: kazack: "Re: Best book on learning C++?"
- In reply to: ma740988: "polymorphic design approach"
- Next in thread: Anthony Borla: "Re: polymorphic design approach"
- Reply: Anthony Borla: "Re: polymorphic design approach"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|