top of page

Dynamic Class Extension

  • Writer: Yi Yin
    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:

ree

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


YI YIN
Austin, Texas

Personal Email - if you like me

*Please replace "_at_" with "@"

kthtes_at_gmail.com

Studio's Email - if you like our games

*Please replace "_at_" with "@"

286studio_at_gmail.com

©2017 BY YI YIN

bottom of page