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.
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
orJPanel
). 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.
0
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.