Question on Design of a Custom IO Package
I am making a package for blocking i/o which I plan to use in many of my future client programs. I have thought about modelling it on Thread and Runnable; that is, you can create a concrete implementation by either subclassing aBlockingSocket class or overriding itsParseable interface. BlockingSocket will implement Parseable, and unlike Runnable, Parseable has many methods. So in my BlockingSocket class, should I implement the Parseable interface as somthing simply like this:
publicclass BlockingSocketimplements Parseable
{
private Parseable p;
public BlockingSocket(Parseable p)throws NullParserException
{
if (p ==null)
thrownew NullParserException("Null parser.");
this.p = p;
}
// Parseable's interface
publicvoid parseNext()
{
p.parse(Buffer b);
}
// Rest of Parseable's interface
// ...
}
In this code I can either extend BlockingSocket and override its interface, or pass it a Parseable argument in its constructor and define its interface that way. Note that I only allow a call to the constructor if it has a non-null Parseable.
My question, to recap, is whether this is a good design. I am modelling it on
the Thread and Runnable relationship, but the difference is that Runnable has a one-method interface; Parseable has like 5+. So I will have to implement all those methods in my BlockingSocket class, where they all just call the Parseable object's methods. It seems a little silly, and I have the feeling that there may be a better way. I thought of doing:
publicclass BlockingSocketimplements Parseable
{
public BlockingSocket(Parseable p)throws NullParserException
{
if (p ==null)
thrownew NullParserException("Null parser.");
this = p;
}
}
but this would erase the non-Parseable interface in my BlockingSocket class (for instance, the connect(), disconnect(), etc.). Is there any way to do this, without changing BlockingSocket's type to Parseable, so that its non-Parseable interface?
I sort of rambled on and my questions may be hard to discern, so I'll restate them:
1) Is it a good design?
2) Is there an easier way to do it, like something I said in the above paragraph?
[3468 byte] By [
Endera] at [2007-10-2 12:51:31]

I think that depends on whether or not there's any specific need to have the Parseable be part of the BlockingSocket. A Thread is a special case. I can't start a new thread of execution using the Runnable interface alone. It literally requires the invocation of the native method Thread.start(). So designing the Thread class to easily allow a Runnable to be used makes sense because otherwise creating a subclass of Thread would be mandatory.
In most other cases you don't encounter that requirement. If a BlockingSocket is an implementation of Parseable then it doesn't make much sense to follow Thread's design. Developers can create their own Parseable implementation without needing to pass it to the BlockingSocket class. Maybe BlockingSocket has additional behavior you wish to be able to reuse, but in that case it may make more sense to simply move that to an AbstractParseable class instead.
So no there's nothing inherently bad in the design, at least not that jumps out at me. I would seriously consider why you feel this is needed though. Thread and Runnable have a very unique requirement that move the design in that direction, I'm not sure if it makes sense in your case or not.
> My question, to recap, is whether this is a good
> design. I am modelling it on
> the Thread and Runnable relationship, but the
> difference is that Runnable has a one-method
> interface; Parseable has like 5+. So I will have to
> implement all those methods in my BlockingSocket
> class, where they all just call the Parseable
> object's methods. It seems a little silly, and I have
> the feeling that there may be a better way. I thought
> of doing:
> > public class BlockingSocket implements Parseable
> {
> public BlockingSocket(Parseable p) throws
> throws NullParserException
>{
> if (p == null)
> throw new
> throw new NullParserException("Null
> ion("Null parser.");
> this = p;
>}
> }
>
First of all, that code is not valid. You cannot assign anything to the keyword this, in fact doing so would be quite nonsensical. Why would you want to? What behavior is it that you think BlockingSocket should have and what behavior is it that you think a Parseable should have?
I guess I also don't see what you are trying to achieve. Blocking I/O exists out of the box. The io package is decently rich in different streams, readers and writers. Chaining together streams is such a common task that it probably does not merit a whole framework.
Now, if you were talking non-blocking I/O, that is a more complex subject and probably worth the investment in a few helper or framework classes, if only so other developers do not abuse NIO.
What are you really trying to achieve?
- Saish
Saisha at 2007-7-13 10:03:55 >

I think I miscommunicated the intent of my package. I see that BlockingSocket is a bad name, I will replace it something BSManager or BSHandler (is "handler," by convention, used for interfaces?) or ClientManager/ClientHandler.
I'm going to explain what the purpose of my package. But this isn't the point of my post. So be sure to jmp to IMPORTANT.
Here are the interfaces for the former BlockingSocket class.
public interface Parseable extends SocketHandler
{
public void parseNext();
public int getHeaderLen();
public int getMessageLen(byte[] header);
public boolean isValidHeader(byte[] header);
public boolean isValidMessageID(byte id);
}
public interface SocketHandler
{
public void setSocket(String hostname, int port);
public void connect();
public void connect(String hostname, int port);
public void disconnect();
public boolean isConnected();
public boolean isLoggedOn();
public void addOutBuffer(Buffer b);
public void addInBuffer(Buffer b);
}
As you see, it is meant to be a package for clients with blocking I/O. It manages the buffers sent/received, and abstracts the protocol and parsing.
IMPORTANT: I have thought about making an abstract class that declares the Parseable interface for its subclasses. But if you subclass then the Parseable interface will be used only within the subclass, it will not be a interface for objects' communication. What's the point of an interface if it's an interface for base classes to talk to derived classes? It's one object, and it uses that interface for private communication, which seems to me like a design flaw. Are my worries wrong? Is this an okay practice? Anyways, this is why I chose to do what I did - it seems more OOP, since interfaces are being applied to communication between objects.
Endera at 2007-7-13 10:03:55 >

I am thinking about having my ClientBIOM (Client Blocking IO Manager, formerly BlockingSocket) not implement Parseable at all, but instead be a final class and take a Parseable as an argument in its constructor, not allowing any instantiation without taking a Parseable. There will be no subclassing, but instead just passing a Parseable to it by its constructor.
Any feedback?
Endera at 2007-7-13 10:03:55 >

Without even analyzing the code, I like it. You are using composition and delegation over inheritance. This frees up your design.- Saish
Saisha at 2007-7-13 10:03:55 >

As for this interface, it seems overly complex for me. You have a lot of 'workflow-related' items that probably instead belong in an abstract class (one of the few good uses of abstract classes, that and providing default encapsulation). As a caller, do I want to call isValidMessage()? Probably not.
Instead, think of it the following way. Your interface is offering a convenience contract for a protocol. As such, actually performing the protocol is best left as an implementation decision. As a caller, I would ideally want something that took an InputStream (or a Socket if bi-directional communication is needed) and returned another Parser (or Parseable) interface.
This interface would simple expose hasNext() and getNext(), ala an Iterator. Like you have alluded to, and which I think is a good idea, the separation of protocol and parser is a good idea. Programming to interfaces is also good, a core GoF precept. Your actual implementation (such as the methods to check validity and message length) could be in an abstract class or concrete class, depending probably more on time than eventual plugability need.
What to do if isValidMessage() returns false in your implementation? Declare your own custom exception and throw it. If you want to use a bit more of the language, declare another, unchecked exception (extending Error or RuntimeException) that will be thrown if IOException is encountered in your implementation.
What would a caller do, realistically, if a network failure occurred? Probably either retry or fail. If retry is needed, you can always catch the unchecked exception and retry. But for most callers, this is a case that cannot reasonably be handled. Why make every caller catch IOException?
The above is just my opinion. There are many ways to skin a digital cat.
Best of luck.
- Saish
Saisha at 2007-7-13 10:03:55 >

