Don't Repeat Yourself (DRY)
The Don't Repeat Yourself principle is one of the most important concepts that a software engineer has in his/her arsenal (in my opinion).
I previously worked on a legacy PHP project whereby a complete disregard for DRY has resulted in a completely unmaintainable codebase. If someone found a small, insignificant bug you would often find yourself fixing the same issue in multiple places.
On the most primitive of levels, if you isolate one piece of business logic and reuse it as appropriate, when there is an issue or you decide to change the business logic then you only need to update your code in one place.
When can you repeat yourself
There is a very interesting Stack Overflow post on this topic here: Is violation of DRY principle always bad?.
My personal view is that DRY is a great principle to be thinking about as you code. In some circumstances however you need to do a simple cost benefit analysis. For me readability is vitally important in my codebase.
A few cases spring to mind from my current iOS project where I have explicitly chosen not to follow the DRY principle. For example:
-
I have some custom
UIView
subclasses which implement an interface to display a loading screen. At the moment they all show the same loading screen using the same code. By duplicating the code it makes each individual class more readable. Furthermore I intend for these implementations to diverge in the future. -
I use a custom dependency factory pattern to abstract the dependencies of various classes. This in itself makes my codebase so much more readable and as such the fact that two similar classes have dependency factories with similar implementations is a worthwhile trade off. Any further abstraction would lose me the benefits of clarity.
iOS and the delegate pattern
The delegate pattern plays a big role in iOS development. The out of the box classes provided by Apple which make up most user interfaces rely on them heavily. For example UITableView
uses the delegate pattern to implement its datasource and its layout. As another example UITextView
has a delegate for indicating when various things happen.. for example the textViewDidBeginEditing:
delegate method.
Extending protocols
On the iOS platform (Objective-C, Swift.. both can) you can extend a protocol. In Swift the code is exactly the same as how you would write a subclass.
protocol SubProtocol: SuperProtocol {}
What has this got to do with DRY?
Very good point.. :P
I recently built a pull to refresh/scroll to load more piece of functionality in Swift. My conceptual premise was that anything I want to refresh/load more of will be in (or can be put in) a subclass of UIScrollView
- UITableView
, UICollectionView
etc.
I figured that I could write a custom implementation of the various scroll view delegate methods to track the users scrolling and show the respective pull to refresh header/load more footer when appropriate.
Unfortunately it was not as easy as I had hoped.. (That is not to say that it was difficult ;) )
Apple give us these various UIView subclasses out of the box, and they are extremely powerful. One can not really complain. That said I don't understand why Apple didnt seperate the table view delegate from the scroll view delegate in relation to table views and collection views.
UITableViewDelegate
is a sub-protocol of UIScrollViewDelegate
which means when you implement the former you have to implement the latter. UIScrollViewDelegate
has no required methods but that is beside the point :)
The difficulty is that I want to implement some of the UIScrollViewDelegate
delegate methods but I do not want to couple them with an implementation of any UITableViewDelegate
methods because I want to use them elsewhere. I do not want to repeat myself !
Resolution
The way to resolve this is simple.
I wrote a class (PTRScrollDelegate
) which implements the UIScrollViewDelegate
protocol. It does various fancy things to make my pull to refresh functionality work.
I then have a custom class which extends this scroll view delegate AND implements UITableViewDelegate
. Simple.
If I want to use the functionality with a UICollectionView
I again create a custom delegate class, extend my scroll delegate and implement the UICollectionViewDelegate
You implement your scroll view delegate methods within your superclass whilst you implement your table view delegate methods in your subclass.
Problems
There is one patently apparent problem with this kind of setup (although it does not present itself in this specific case).
That is the fact that Swift does not support multiple inheritance whilst it does support the implementation of multiple protocols.
Consider the situation where you have a protocol which extends three additional protocols. You would not be able to keep the implementations of the various delegate methods in independent reusable classes in this case.
That said I can not think of (off the top of my head) a situation within the various provided iOS delegates where this would be a problem. Anyone know of any?
If you are in a situation where this is an issue for you there is more than likely or more appropriate way of structuring your design.
On that note..
Conclusion
The above is essentially a personal case study of my approach to avoiding code repetition on the iOS platform (in one particular situation). Hopefully this will be of use to somebody.
If you have any questions, comments, or suggestions for future posts then I would love to hear from you :)