Domanda

I found something which I had never think about before when going through the code base of the new company I just joined. Basically this what happens: we have a base class which represents say an Image with several abstract methods to convert between different image types. The different image types are subclasses of the base Image class. Subclasses are responsible for converting between different images which are all derived classes.

My question is is this supposed to be a good design? if not what alternatives can we have?

Thanks a lot.

È stato utile?

Soluzione

If I designed the scenario then I would do the following.. ( N.B i am not a very good designer, just sharing my concepts)

  1. a base class image with abstract methods which wil only represent the behavior not any actions to convert
  2. subclasses implementing those abstract classes which define the behaviors
  3. other utility class which have methods which convert between different images.

Altri suggerimenti

It's not the most elegant structure, but it can work--so long as you're dealing with only a few image types. Shifting conversions to be a generic capability of the common base class would be more elegant, and more scalable to additional image types. The downside is that introduces change, and carries some design tradeoffs of its own.

Key architectural questions are:

  1. Where? Where do you put the knowledge about how to convert type X to type Y? Inside type X? Inside type Y? Inside a common ancestor? Or within some separate conversion facility?
  2. How? Should X know how to export directly to Y? Or should Y know how to import from X? Or should X export into a generic/common format, which Y then knows how to import from?

You can find every one of these options in the wild.

Your current design, with conversions inside each image class, mirrors fairly basic data types/classes in many languages; they are each expected to be able to serialize, represent, or convert themselves to several other formats (e.g. to string, to an over-the-wire format like pickle, etc). But if there are more than a few types, writing from-X-to-Y converters will quickly grow unmanageable. You will need n(n-1) conversion methods--"ain't nobody got time for that!"

Does-it-all conversion and manipulation suites like netpbm and pandoc, on the other hand, often use a generic intermediate representation. The closest analog I know to your situation, imaging libraries like Python's PIL/Pillow, often define a generic base class (e.g. Image) that take on the role of a generic format / data model, even if they don't have their own on-disk format.

Abstractions are invariably imperfect. When you're just dealing with X or Y, inheritance is dandy. But when you need to deal with the intricacies of type X and type Y simultaneously, there's no perfect answer. Decomposing with further classes won't help; nor will multiple inheritance.

The paradigm and probably best overall answer is moving conversion up into a base class. But this isn't without its own tradeoffs. The base class then has to know about and accommodate conversions for all possible subclasses. So even when you're converting formats that don't have alpha channels, CLUTs, Z-buffers, gamma-correction tables, HDR-calibration information, and other data/metadata, the base class methods and data structures must know about and deal with such things if any of their subclasses have those things. Pushing conversion up-stack can also lead to ping-pong conversations between base and sub classes (both in the design and at runtime), as the base methods seek to interrogate, or delegate tasks to, the subclasses. But, these concerns about housing conversion up-stack are second-order issues. They aren't too vexing--unless and until you actually encounter them.

So net advice is: For a few image types, conversion can reasonably live in each of the image classes. This is especially true if you already have "a bird in hand"--working, tested code that already takes this approach. But if you want to "clean up" the design, shifting import/export/conversion to a base class is more elegant and more scalable to additional image types. (Before you begin that march, make sure you have good unit tests on the existing implementation, so that you can readily qualify the second-generation code.)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top