1

I have a two different ways of representing data in my app: via UITableView or UIScrollView.
So I have 2 main classes: AppTableView: UITableView and AppScrollView: UIScrollView. And I want to implement the same additions to both views. So I wrote two classes: SomeAdditionsTableView: UITableView and SomeAdditionsScrollView: UIScrollView. The code of this classes is the same. Main classes now looks like
AppTableView: SomeAdditionsTableView and AppScrollView: SomeAdditionsScrollView.

How to avoid this code duplicate? Thanks in advance.

yury.ku
  • 1,188
  • 1
  • 10
  • 19
  • what do you have in both classes? – Omar Abdelhafith Jun 19 '12 at 12:17
  • 1
    If it helps you, you can make only one SomeAdditionsScrollView:UIScrollView and remove the SomeAdditionsTableView: UITableView because the UITableView is a UIScrollView. Then you can use the SomeAdditionsScrollView as superclass to both AppTableView and AppScrollView – Adrian Ancuta Jun 19 '12 at 12:21
  • @AdrianAncuta I don't believe that will work. If you subclass UIScrollView (SomeAdditionsScrollView:UIScrollView) UITableView will still directly subclass UIScrollView and will not get the benefit of SomeAdditionsScrollView. If AppTableView subclasses SomeAdditionsScrollView it will no longer be a tableView. – Aaron Hayman Jun 19 '12 at 12:38
  • @Aaron Hayman You are perfectly right. Then it should try to make SomeAdditionsScrollView:UITableView. So that way both of the are going to be what they need to. – Adrian Ancuta Jun 19 '12 at 12:40
  • @Adrian Ancuta, thanks for your answer. But if I use SomeAdditionsScrollView as a superclass of AppTableView I can't work with UITableView properties and methods (separatorStyle, separatorColor and so on) in AppTableView. – yury.ku Jun 19 '12 at 12:44
  • if SomeAdditionsScrollView is a subclass of UITAbleView then you should be able to use your methods – Adrian Ancuta Jun 19 '12 at 12:51

3 Answers3

4

Yeah this is a problem with the lack of multiple inheritance in Objective-c. I had the same problem when needing certain methods on a subclass of UIView and UIScrollView separately here: Subclassing UIView vs UIScrollView. There are 3 possible solutions I know of:

  1. If you don't need to store any kind of instance variable, simply declare a category on UIScrollView and make sure to import that category into the two subclasses. This is the easiest solution, but least likely to work since you probably need to store state information if you're subclassing anyway.
  2. Only create a subclass of UITableView and simply don't use it as a UITableView when you don't want a UITableView. You can technically just use a UITableView as a UIScrollView without invoking any of the tableView's methods. Of course, you're going to end up carrying around the 'weight' of a tableView (all of it's instance variables) but there no reason you have to use a UITableView as a UITableView and not just a UIScrollView.
  3. Delegate as much of your code to a separate object to minimize code duplication. In each separate subclass carry an instance variable that is the method delegate and forward method calls to that delegate. Now here's where it gets fun. You can use protocols to declare the delegate methods in your subclass and override a special NSObject method: - (id) forwardingTargetForSelector:(SEL)aSelector to make sure those method calls get sent to the delegate. You use a category on the subclass that conforms to the protocol declared in the delegate class. This will expose all the methods of the delegate class in the subclass without requiring you to actually implement those methods in the subclass. When the runtime can't find the declared method in the subclass, it will call - (id) forwardingTargetForSelector:(SEL)aSelector, which you can use to return your delegate/forwarded class. This will prevent you from needing forward each individual method. Depending on what those method calls do, this may take a little more 'wiring', but it'll save you a lot of code writing in the end. It essentially 'mimics' multiple inheritance in objective-c using protocols. See my question/answer here for more details: https://stackoverflow.com/a/9419587/1147934.

Of the three, the last option tends to work the best for me. It takes a little work to get your head around but will significantly reduce code duplication. I also use it when I want to subclass without subclassing. The biggest requirement, though, is any class that you want to do this with will have to move it's method declarations out of it's interface into a separate protocol. But it's really not a big deal and the benefits of getting 'multiple inheritance like behavior' is great.

Also, there are times you may need the forwarded class to access instance variables in the forwarding class (the subclass). You can achieve this by using a delegate pattern whereby the forwarded class maintains a weak reference to the forwarding class in order to access those instance variables. For example, in your case, if you're trying to delegate methods that operate on a UIScrollView, those methods may need to be able to access that view. If those methods are stuck in a delegate class, they won't have direct access to the view's variables unless you give it to them. As usual with any delegate pattern, be very careful you don't create a retain cycle.

Community
  • 1
  • 1
Aaron Hayman
  • 8,492
  • 2
  • 36
  • 63
  • I think method 3 is sort of an implementation of mixins for languages with single inheritance; I've used that in PHP before; in MI languages, mixins are really powerful and eliminate a ton of code dup. I actually think mixins could be used to implement aspect-oriented programming. Anyways...I like your approach. – Clayton Stanley Jun 20 '12 at 03:05
0

If your additions don't need any state of their own, you can make them a category on UIScrollView. Then, since a UITableView is a type of UIScrollView, you can use the category methods on one of those too.

If they do need to define new variables, then I would make it an independent class and have a MyTableView : UITableView subclass with a SomeAdditions property and, similarly, MyScrollView : UIScrollView.

Andreas Ley
  • 9,109
  • 1
  • 47
  • 57
Phillip Mills
  • 30,888
  • 4
  • 42
  • 57
0

You can achieve a lot by using protocols and "has_a"-relationships, instead of inheritance's "is_a"-relationships.
One very common pattern is delegate, but protocols are also useful for forwarding methods calls to encapsulated or wrapped objects.
in the following example to classes, that are not related to each other, share a common object, but it is also possible, that objects of the same kind use objects of different classes, that all implement a common protocol, so equal objects could do very different stuff.

@interface ClassA : NSObject
@property (strong) id<BrainProtocol> *brain
@end

@@implementation ClassA
@synthezise brain;

-(void)theMethod
{
    [brain theMethod];
}
@end

@interface ClassB : NSObject
@property (strong) id<BrainProtocol> *brain
@end

@@implementation ClassB
@synthezise brain;

-(void)theMethod
{
    [brain theMethod];
}
@end

ClassA *a = [[ClassA alloc] init];
ClassB *b = [[ClassB alloc] init];

//A object, that implements the BrainProtocol

Brain *brain = [[brain alloc] init];
[a setBrain:brain];
[b setBrain:brain]; 
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178