Dynamic Class Extension
- Yi Yin
- Apr 24, 2018
- 3 min read
Updated: May 2, 2018
This article talks in Objective-C, but the thought of Dynamic Class Extension can be applied to any dynamical languages.
Background
Many advanced programming techniques involve a common generic thought: making static components or mechanics dynamic. By doing this, the amount of code is effectively reduced, meaning that the chance to make errors and bugs is also reduced. That's why we always prefer the more concise implementation.
Many dynamic languages (especially scripting languages like Lua, Python and JavaScript) offers mechanism that allows you to change properties (sometimes even including functions, as in some languages functions are of the first class) at runtime. People may ask, why don't we use subclasses? For instance, in C++, if we want to extend a class, we can derive a subclass from that base class.
The reason is that deriving a subclass sounds "heavy". Subclass + superclass, two classes, 4 files - it's a significant psychological burden. There is one thing that programmers often ignore: the complexity is not only on the amount of lines of your code, but also on the amount of files. If every time you change something, you have to look through 4 files (Superclass.h, Superclass.cpp, Subclass.h, and Subclass.cpp), then we say it is "heavy". Moreover, sometimes adding a function to an existing class dynamically will significantly simplify your code. Now, let's see an example -
Problem
You are working on a 2D game (or a productivity app that involves some usage of sprite-like graphic objects). The engine (iOS API or 3rd party, whatever)'s class hierarchy looks like this:

You then realize that the base class Sprite does not support custom anchor points (that happens in Qt QML), or the default behavior of the anchor point makes it difficult for you to develop on (that happens in cocos2d). You want to add a new function to the bass class Sprite, and you hope all its subclasses (Image, Shape, GLEffect, Ellipsoid, Rectangle, etc.) could use that function.
Clearly, it'd be a pain in the ass to derive your own MySprite class in a trivial C++ way. Let's say you derive a new class MySprite from Sprite, and you add anchor-point-related functions to it. Soon you'll discover that Sprite's subclasses can not use those anchor-point-related functions. You have to either hack the engine code, or derive a lot of new subclasses e.g. MyImage, MyShape, MyGLEffect, MyEllipsoid, MyRectangle, etc.
Of course, in C++ we could figure out another way (for an easy walk-around, we can create a group of auxiliary functions that take a Sprite* as input) to solve that issue. But for here, let's see how a dynamic language solves such an issue -
Solution - Partial Implementation
We extend the Sprite class like this (pay attention to the bold text) - here we add 3 functions and 1 property as below:
[Objective-C]
// put it in any .h you like - don't have to be a separate .h file
@interface Sprite (MyAddition)
@property (getter=getMyAnchor, setter=setMyAnchor) CGPoint myAnchor;
-(CGPoint)getMyAnchor();
-(void)setMyAnchor(CGPoint anchor);
-(void)resetMyAnchor();
@end
// put it in the relative .m file (corresponding to the .h file of the declaration part)
@implementation Sprite (MyAddition)
-(CGPoint)getMyAnchor() {
// calculate MyAnchor according to the current sprite's geo
}
-(void)setMyAnchor(CGPoint anchor) {
// your own implementation
}
-(void)resetMyAnchor() {
// your own implementation
}
@end
This is called "partial implementation". Even if this class is a system API, you may still dynamically insert new functions and new properties. After that, all its subclasses could then make use of them:
// in the code where you use its subclasses
// we assume Image is a subclass of Sprite
Image* im=[[Image alloc] initWithPath:"my_image.png"]; // create an image sprite from file
im.resetMyAnchor(); // subclass instance calls your DIY function on its superclass
im.setMyAnchor(CGPointMake(0.2,0.7));
im.myAnchor=CGPointMake(0.2,0.7); // also calls the setter function, identical to the above
You can see, from the above, it is also possible to add a property to the superclass, and then all its subclasses can use it, just like using your DIY functions.
Again, you can do similar things in other dynamic languages like Python, Lua and JavaScript. However, those are actually table/prototype based languages. They implement OOP using table / prototype. I'll talk about them later in the future "Functional Programming" series.
Comments