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?