5

For those unfamiliar with the SOLID principles you can start reading here: Wikipedia article. However, most of my understanding comes from: http://www.objectmentor.com/resources/publishedArticles.html

Regardless, it seems that every GUI interface I run into in Java (even the one in Adobe Flex honestly) is filled with dozens of methods which are duplicated across many classes. Deep inheritance hierarchies are the norm and composition is definitely not preferred over inheritance. This seems to be a clear violation of SRP and ISP.

What I would like to know is, am I correct in thinking that these frameworks are violating the SOLID design principles? If so, then why?

Response to Snowman

I have to disagree that SWING is an example of SOLID. Lets use the JButton example:

1) SRP: The method isDoubleBuffered goes beyond the responsibility of JButton to represent a UI button. That method represents an implementation detail that has nothing to do with abstract concept of a UI button.

2) OCP: If I want to change how JButton handles events I have to replace the JButton class completely.

3) LSP: The implementation of JButton cannot be readily substituted with other implementations. JButton inherits most of it's implementation details directly. If I want to change the way a JButton creates tooltips, assigns action listeners, or access the graphics context, I have to throw away all of SWING. I cannot simply drop in a new class that handles those things since JButton inherits directly from JComponent.

4) ISP: The JButton public interface is littered with dozons of methods from every class that it inherits from. It is litterally a dumping group of public methods from JComponent, Container, Compound, etc. If the interface of JButton adhered to ISP there would only be 10 or so publically exposed methods.

5) DIP: JButton publicly exposes methods of it's concrete implementation details. If it applied true dependancy inversion then things like icon's, layout, event handling, etc would be delegated to separate classes that the user could readily switch out to achieve new functionality.

  • 2
    Why do you feel that your observations (duplicate methods, deep inheritance hierarchies) violate SOLID principles? Can you be more specific? As a general rule, GUI API's do use inheritance extensively; I think it's fair to say that GUI's are the dominant use case for inheritance hierarchies. That said, I've always found Java API's to be more convoluted than they need to be. – Robert Harvey Oct 9 '14 at 16:27
  • @RobertHarvey hopefully my new edit helps clarify. – Bernard Igiri Oct 9 '14 at 18:33
  • If isDoubleBuffered is your best example of a violation of SRP I'd say they did a good job. As for ISP, you comments make no sense to me. JButton is a class, not an interface. Of course it is littered with all the APIs of its parents. Same for OCP - it handles events by calling ActionListeners. Seems pretty open to me, but I must be missing something in your arguments. Please elaborate. – user949300 Oct 10 '14 at 20:53
3

In general, I disagree that GUI frameworks violate SOLID. There may be exceptions, but this wraps up my experience with multiple frameworks:

  • SRP: a GUI class typically has one responsibility. Maybe it is responding to an event, rendering an object, or maybe it is "represent this UI element." That last one might sound like it is not a "single" responsibility because that one responsibility is to tie everything together (e.g. JButton or JPanel). The key to look for is those "big" object often delegate to other objects that are specialized and handle one aspect of the object in detail (event handling, rendering, etc).

  • Open/Closed: Your example shows how UI classes are definitely open for extension: there is a lot of inheritance going on. One could argue that UI classes violate the "closed for modification" aspect since they are typically very configurable and can be modified to do a lot of things differently, but that is one purpose of these classes to begin with.

  • Liskov Substitution: in pretty much every framework I have seen, subclasses add more functionality and still allow parent class behavior. It does not always make sense, but it is supported. For example, you could nest UI elements inside of a button if it extends a frame (perhaps one could add an icon to a button this way, a picture element nested inside a button despite "button" not being commonly thought of as a parent element). This principle is key to the nested nature of UI frameworks.

  • Interface segregation: my main experience here is with Java, where there are many little interfaces for various listeners and handlers. Seems like a good fit.

  • Dependency inversion: along with the Liskov argument, many frameworks do rely on abstractions. For example, there may be some high level interface or abstract class to represent all UI components. Parent components can then accept any high-level component as a child, which allows arbitrarily complex layouts via nesting.

Overall, I think there is a strong argument for GUI frameworks adhering to SOLID.

2

In general, SOLID principles are usually met (See Snowman's answer)

But since you mentioned the Android SDK, I will go a little into detail on how SOLID principles coming too short in this particular API.

Single responsibility principle. The View class is responsible for

  • positioning
  • drawing
  • event processing
  • handling a huge load of other callbacks.

So, if you consider the SRP as "A class should only have one reason to change" - you already lost the argument. They tried it by creating ViewGroup and making the ListView use an Adapter for the actual data. These things actually work quite well.

Open closed principle. Although basically open for extension, unfortunately only via inheritance. There's no way no modify behavior (e.g something in the drawing or layouting process) via strategy-pattern or something similar. Everything is a constant, so you have to choose between the existing options.

Liskov substitution principle is of course met via the java language requirements. I do remember though, that we had issues with TextView or EditText in the past, were the inherited method returned the same type and was named the same; behaved differently anyway. The post-condition wasn't met. Too bad I don't remember exactly what it was. Something with the Focus or same properties having different result or something.

Interface segregation. What should I say? The basic View interface is polluted with methods over methods. Why is the scrolling implemented (and accessed) via the View class? Remember the callbacks I mentioned before? But in general, if you're trying to "implement" the View "interface" (extending the class) you only need override the methods you really need - but this is more like a language feature in java. If you see it more pedantically, you'll actually implement scrolling by overriding the class even if you don't need it.

Dependency inversion seems to be ok in android. But I'm sure that if you really search for it, you'll find enough spots in the framework were something depends on an implementation instead of an abstraction. I'm not sure whether the 34 instanceof calls in the View class are a sign of good abstractions.

In general, the android sdk tries its best fulfilling all principles. I also know that it's fairly large and also needs to take performance into account, so it's harder to make it fulfill all principles.

I should add, that I probably wouldnt be able to do any better.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.