Home > Software engineering >  How can I deal with a fat interface?
How can I deal with a fat interface?

Time:06-23

Say I have a big interface IShape of which Circle, Square, Triangle... inherit from.

IShape has become very big, and has functions dealing with unrelated topics: for example, many for dimensions calculation, others for moving and animations, others for colouring, etc.

This is against the Interface Segregation and Single Responsibility Principles, so I was trying to find the design pattern that better suits my case, but I am not sure about what is the best way for me to proceed.

I was thinking in breaking IShape in smaller interfaces: IDimensions, IMovement, IColour... and then making Circle, Square and Triangle inherit from them. This would solve the problem of the fat interface (although the implementations would still be very big).

What is the approach I should follow?

CodePudding user response:

One approach is to keep the object (IShape) fairly "dumb" (keeping track of its internal state only, plus boilerplate access functions) and then add free-standing functions acting on it in more involved ways. In some contexts it might be useful to integrate these functions into class interfaces of their own (particularly if the functionality can be grouped well and requires some internal state), but in my opinion and experience, there's nothing wrong with a library of free-standing functions acting on objects. Note for example that large parts of the STL are made up by non-class functions.

edit: Note that free-standing functions are basically automatically re-entrant (thread safe). With member functions, you have to pay more attention that the object state does not change, you might have to lock some sections, and so on.

edit2: I generally try to favor composition of objects (object A "has an" object B) over inheritance (object A "is a type of" object B). When you think about it, a square (which "is" a shape) might "have" a color, but it "is" not a color. Also, think hard about on which level you compose objects: Does every Shape have a color and/or dimensions (in which case the Shape base class should have Color and Size/Polygon/Geometry/BoundingBox/... members), or do you want to keep the Shape interface more abstract in your code ecosystem (in which case concrete geometric shapes inherit from (abstract) Shape and additionally have further concrete properties, such as Size and Color)? This (and the concrete implementation of e.g. Size/Geometry) depends mostly on what you want to do with your shape-like objects.

CodePudding user response:

I'm backing up the answer from Daniel and also would like to add a few tidbits.

The third principle I believe is applicable is Separation of Concerns (https://en.wikipedia.org/wiki/Separation_of_concerns). It says everything and nothing and it also takes alot of time to get a mental hang on. I'm lacking much info about your application so I'm shooting in the dark, but, as an example of reasoning:

Why do you want to associate a circle and a square? What are their comonality? Besides having lines that encircles a 2D area they don't have much in common. A circle have a radius, a square have none. In a square the angle between the legs are 90 degress - a circle has nothing of the sort. You can't describe the two using the very same set of explicitly, well defined variables.

So, why do you want to put them in the same basket? When you have the answer to that, or more probably the answer why not to have them in the same fat interface, I think you are on a good way of understanding your design.

Hope you find your way! /Nils

  • Related