tags:

views:

48

answers:

2

I am designing a library for my application, that wraps Windows smartcard subsystem.

Physically, there is a smartcard, that is connected to smartcard reader and the reader is connected to PC. There are many types of smartcards (Javacards and .NET cards for example). One idea to design this is as follows:

TReaderListBase <- TReaderListJavaCard

TReaderBase <- TReaderJavaCard

TSmartcardBase <- TJavaCard

List of readers' Get() method returned reader by index or by name, and the method Connect() of the reader returned the card object instance to work with.

The problem is that while there are physically really different types of smartcards, there is only one reader type actually, that can read them all. TReaderListJavaCard was there only to perform search functions using specific Javacard attributes. And TReaderJavaCard is there only to return an instance of TJavaCard class instead of TSmartcardBase, returned by TReaderBase in Connect() method.

Also this design has some logical holes like, for example, when you plug smartcard out of reader, it may seem logical, that TSmartcardBase descendant instance is immediately destroyed, because class name says, that it impersonates card itself and this card is no more.

Tt works for sure. But does this scheme seem logical?

I had a suggestion from my friend to redesign a system as follows:

Skip readers, create a TSmartcardProxyObjectBase class, descend TJavaCardProxyObject from it and add all methods on searching and connecting cards to them. Something like all-in-one package. This abstraction looks good because indeed CardProxyObject can search for something, connect to something, be offline (if card, to which it is connected is removed) etc. But this design violates SRP because one class does both communication and object searching. Should I want to detach searching functions from it and I get the same scheme as in the first case (it will just add something, that can work with readers).

My friend says, that we do not actually work with readers directly and the main goal of library is to work with cards so we should hide readers from library user.

Since such projects is something you can learn OOP design from, I am interested in finding a crystally beautiful solution ;)

I hope I explained all understandable ;)

There is the third way to do that. Let it be final ReaderList and Reader classes. But there should be no Connect() method in reader. Instead I can implement Use() or PutOnline() method in smartcard class to use this particular reader for communication effectively hiding low-level SCardConnect call from user. Searching functions could be moved into some helper class aside (like TJavaCardLocatorService). Or should I made them static members of TJavaCardProxyObject class? Something like TJavaCardProxyObject.AttachToCardWithAID(ReaderList), but the name looks also illogical and again seem to violate SRP.

What do you think? Is that in any way important to have nice architecture in real life projects or I am just waisting the time trying to learn something unimportant?

+2  A: 

Is it in any way important to have a nice architecture in real life projects or I am just wasting the time trying to learn something unimportant?

If you're buliding a project, your user interfaces are very important.

If you're building an OOP library, your API is very important.

In both cases, the question you're trying to answer is, "Is this (API, screen, report) easy to use? easy to read? easy to interpret?

It's definitly worth the time to make an API as useful as possible.

As to your title question, no, it's not necessary for an OOP abstraction to be as close to life as possible. However, the closer the API is to the way people use real-life objects, the better.

Gilbert Le Blanc
+1  A: 

Your friend's statement is basically on the right track.

My friend says, that we do not actually work with readers directly and the main goal of library is to work with cards so we should hide readers from library user.

It doesn't matter if your friend is correct or not; this is exactly what you should be thinking about when you write the abstraction. How will the library user be thinking about the task they're using the library to accomplish? Figure that out, then implement the abstraction as closely to that as possible.

Regarding violations of SRP when building an "all-in-one package", currently the model for programming has the "Programmer" as responsible for not only writing the code, but also compiling, loading, running, and collating the output of the code. It wasn't that long ago when these secondary tasks were handled by an entirely different person (or possibly multiple people). I'm sure there are some people on SO who remember taking punch cards down to the computer department. Which model violates SRP? Answer: Neither. The concept of what a programmer is responsible for has changed in the last 30 years.

If you find an model that better fits that changes the concept of single responsibility, that's okay because the mode is probably a better abstraction for your library users.

perigrin