In-Reply-To: Danny's Object Oriented Programming Access Modifiers

Abstract

Danny van Heumen, my co-worker at GROUP9, has written a series of blog on his thoughts about Object Oriented Programming. In this blog I share my views on his ramblings ;-). The TL;DR version: I argue that his notion on access-modifiers and seemingly strange design choices are not caused by the design choices themselves but by not viewing OO design at the package level and the often seen way of defining packages in Enterprise applications where packages don't expose a clear, minimal API towards other packages.

On access-modifiers

At GROUP9, we've been discussing a
 bit about object oriented programming and designs and how to do it "right". In his post on access modifiers as part of a larger series of posts on OO, Danny argues a lacking access modifier and argues that the protected access modifier (the context is Java here) allows to wide access. I'm missing the actual explanation of which access modifier he's missing, however I'm presuming he's referring to the protected access modifier as defined by C#, which only allows access by subclasses.

I have to say I kinda disagree with him on this, as I think the designers of Java had specific thoughts on the definition of these access modifiers in the context of packages, which can be seen as a sort of mini-modules.

I've written this response before reading to the whole series, in the light of sparking the discussion, so I might have missed some of Danny's reasoning in further series.

The case for package level APIs

Danny's take on OO-design focusses on the design of the object itself, whereas I think what's lacking in this discussion is the design of the object in the context of a set of objects in a package. When thinking about Object Oriented design (this was somewhat way back), it occurred to me that in many Java EE projects I've come across a lot of Java developers hardly seem to think of packages as a way to really structure their code into various sub-modules.

The most clear example of this is the distinction between how you separate code into various packages, either based on common technical goals or based on the functionality they provide. A lot of projects I've seen separate their Objects into various technical packages, often called jpa, dao, service, etc. The result is that these packages don't offer a clear API, the packages are just used to group a set of classes which happen to share the same technical third-party dependency (JPA, Spring, etc) as a common denominator.

The other (in my opinion better approach) is to separate packages based on functionality. This implies objects with various technical goals can be present in the same package: they collaborate together to provide a specific functionality. From a development perspective, this seems more logical to me, as most of the time, I'm developing some specific functionality, extending the business value provided to the users of the system in some way. Changes to the system where you rip out a complete technical layer and replace it with another implementation are far more uncommon, therefore the separation into technical packages provide an organization I hardly even use.

Using access modifiers when thinking about package level APIs

If you take the notion of functionally separated packages one step further, one could argue that each package has a public API, which provides the functionality accessible by other parts of the system. The objects in the package collaborate (due to the SRP) together to achieve this functionality.

To test this way of developing an application (as opposed to just using packages which are functionally separated), I've created a plugin once for Atlasssian Confluence to see how it holds up in practice. This plugin was the NewCode macro, aptly named NewCode as there was also an old Code plugin ;-).

(This project has since been donated to Atlasssian where they are now using it in their own product and doing the development themselves, so the code publicly available may be in a somewhat abandoned state.)

Developing the project I had a few guidelines set-out for myself:
  • Each package should have a clear functional goal. The objects within a package collaborate to achieve that functional goal.
  • The default visibility of each method, or class within a package should be default (hey, that sounds logical, right :-))
  • Each package has a single or minimal set of Objects with public visibility, both at class and method level (i.e. they are the only ones who can be instantiated from the outside), that provide the API of that package.
Developing the project in this way forced me to really think of a clear separation between the various packages, the functionality they needed to expose and the API. In essence, it forced me to really think about High Cohesion, Low Coupling, at the package level. The end result I think was a codebase which was clearer to understand, as it was directly clear where there was impact when I needed to change any functionality.

In-reply-to: why protected may not be a bad idea

Given this setup, default visibility really makes sense to be the default. Within a package, Objects are closely bound to each other, and need low-level functionality provided by them to achieve their common goal. For the API, only a minimal set of methods and classes should be defined as public. Some part of the API may be exposed as base classes, which can be extended by other packages. However, the functionality provided in these methods may also often be needed by other Objects in the same package, warranting the choice to also expose protected methods to other objects within the same package.

An example design to illustrate this:


Package B provides an API in the form of a specific API class. It also provides a base classes needed by the API to do it's work. Therefore, it needs to access the protected methods of the base class, methods which we do not want to expose to any other object outside our package, as only the API class forms the outward facing API of our package. Package A has a subclass of said BaseClass, implementing logic specific to that package, but adhering to the contract as set out by the BaseClass. The Client classes uses these classes to provide it's own functionality, but doesn't need access to the protected methods. This class may even reside in a third package C, where the protected access level restricts access to these methods.

To make the example more concrete, we can use the already over-used example of Table classes, where the BaseClass is a base class for Tables, the API Class provides generic logic over tables (iterating, aggregating results, etc), and the SubClass provides the concrete implementation for a Table in the MySQL database.

In conclusion

Although I do understand where Danny's view on access modifiers comes from, I think his notion stems the way enterprise applications are designed at the package level. Granted, designing enterprise applications in the way I outlined above can be challenging given multiple tiers, large scale applications, etc. However, thinking about packages as mini-APIs allows for applications which are much easier to adapt, where each package provides a clear set of functionality and therefore changes to the functionality are often very local. In this sense, this way of package design can be viewed as a low-level form of MicroServices, where each service also provides a specific, small set of functionality. But that is food for thought for a whole different blog post :-).

Comments

Popular posts from this blog

Validations across micro-services

Bridging the world of Event Storming and BPMN

How Lombok can throw you off guard - Java Compiler fun