Given an Objective-C class MyClass which communicates with an API/Datalayer/external-something via a class Connection, MyClass should either (depending on surrounding arch) receive and instance of Connection in the constructor or an instance of Connection should be passed to each method call which requires a connection. This way MyClass can encapsulate application logic and remain ignorant of the connection details.
To reuse the logic in MyClass with different external components or to unit-test MyClass, it is proper to use a protocol (ConnectionProtocol) instead of a class Connection. This way, different classes (say ConnectionHttp, ConnnectionAPI2, ConnectionTestingEmulateProxyIssue, for instance) can be used with MyClass as long as they implement ConnectionProtocol.
Now, assume the developer has a block of code which is repeated in MyClass, MyOtherClass, and which performs a series of operations with ConnectionProtocol. This violates the DRY principle and is therefore unacceptable.
Since
MyClass,MyOtherClass, and do not share a common base class (other than NSObject), the developer cannot simply merge the functionallity into a base class.Adding a method to
ConnectionProtocolviolates DRY again as the functionality needs to be repeated in each implementation ofConnectionProtocol.Making the method an
@optionalmember ofConnectionProtocolstill does not work because- the method is used frequently and will need to be implemented in each implementation and
- the method logic is specific to the application, not the connection violating SoC.
The developer considers adding a category to NSObject which checks if the passed object implements protocol
ConnectionProtocoland then performs the required operations. This has drawbacks:- The method space for NSObject is polluted for all objects in the files in which the category is used,
- Errors in which the category method is invoked incorrectly are caught a runtime and the developer picked a strongly typed language for a reason
ConnectionProtocolis extended using a category. This does not violate the DRY principle and respects SoC. It does however violate the Objective-C specification.@implementation id< ConnectionProtocol > (AppLogicMethods) -(void)specialMethod:(NSObject*)myParam { // Do Stuff } @end
How does one go about solving this problem without violating DRY, respecting SoC, and meeting the limitations of Objective-C?