Skip to content

Instantly share code, notes, and snippets.

@0x1mason
Last active August 29, 2015 14:06
Show Gist options
  • Save 0x1mason/0b9ef9935e3bf5446955 to your computer and use it in GitHub Desktop.
Save 0x1mason/0b9ef9935e3bf5446955 to your computer and use it in GitHub Desktop.
Pseudo code example of lib/devices refactoring using a polymorphic approach. I also tried to use class names that might work with changes bootstrap suggested.
The files will make the most sense if you read them in this order:
device.js
android-base-device.js
android-uiautomator.js
others.js
I hate how gist doesn't give you the ability to keep the files in the original order.
//// android-base-device.js
// abstract class that contains common Android code — i.e., functions in android-common.js.
internal_to_libs/devices abstract class AndroidBase: extends Device {
// android-common.js stuff
}
//// android-uiautomator.js
// contains device-specific code. this is a concrete implementation. includes all the code in android.js
internal_to_libs/devices class AndroidUiAutomator: extends AndroidBase {
AndroidUiAutomator.prototype.constructor = function () {
console.log("I'm a concrete class. Thanks for creating me.")
}
// override base class functions when needed
AndroidUiAutomator.prototype.pressKeyCode = function (keycode, metastate, cb) {
this.proxy(["pressKeyCode", {keycode: keycode, metastate: metastate}], cb);
};
AndroidUiAutomator.prototype.longPressKeyCode = function (keycode, metastate, cb) {
this.proxy(["longPressKeyCode", {keycode: keycode, metastate: metastate}], cb);
};
AndroidUiAutomator.prototype.keyevent = function (keycode, metastate, cb) {
helpers.logDeprecationWarning('function', 'keyevent', 'pressKeyCode');
this.pressKeyCode(keycode, metastate, cb);
};
// … and so on
}
//// device.js
// factory method
public static Device getDevice(string deviceName, delegate cb) {
Device device = null;
if (deviceName === “Android 99.99.99”) { // example only—in RL this will be a real device identifier
device = new AndroidUiAutomator();
} else if (deviceName === “Android 1.1.1”) {
device = new AndroidSelendroid();
} else if (deviceName === “iOS7.1”) {
device = new iOS();
} else {
cb(new Error(“Unknown device!”));
}
return device;
}
// abstract class that exposes public interface used by all devices. It defines the least Device knowledge accessible
// to objects outside lib/devices. Eg, in controller.js, req.device.method will always be defined in the Device class. This
// allows for de facto polymorphism in controller.js.
public abstract class Device {
Device.prototype.constructor = function () {
throw "This is an abstract class, you cannot create me directly."
}
Device.prototype.configure = function (args, caps) {
// same code as now
};
Device.prototype.setArgFromCap = function (arg, cap) {
// same code as now
};
// … and so on for all the functions currently in device.js
// Device should also include the functions in common.js (assuming they’re all common) and any other function common to all devices
Device.prototype.parseElementResponse = function (element) {
// same code as now
};
Device.prototype.getAtomsElement = function (wdId) {
// same code as now
};
// … and so on for common.js
// now for the abstract functions. they are overridden in derived prototype when appropriate.
// Overrides can call the base prototype function if needed:
//
// DerivedDevice.prototype.foo = function (bar) {
// var wu = Device.prototype.foo.call(this, bar);
// return specialLogicToChangeWu(wu);
// }
//
Device.prototype.pressKeyCode = function (keycode, metastate, cb) {
cb(new NotImplementedError(), null);
};
Device.prototype.longPressKeyCode = function (keycode, metastate, cb) {
cb(new NotImplementedError(), null);
};
Device.prototype.keyevent = function (keycode, metastate, cb) {
cb(new NotImplementedError(), null);
};
// … and so on for any function invoked outside lib/devices, e.g. in controller.js
}
//// android-selendroid.js : same pattern as android-uiautomator.js
//// iOS.js, FireFoxOS.js, WindersFon.js, PhewturPhone.js, ad infinitum: No need to worry about “Not implemented” handling.
//// Just override the functions you need to implement for the platform and ignore the others; same pattern as
//// android-uiautomator.js
//// controller.js
// somewhere we do something like this and carry on as before.
var device = require('device');
req.device = device.getDevice(req.data.deviceName); // again, just an example. the real prop is prolly different.
// We proceed polymorphically--we treat req.device as if it were prototype Device, even though the object will
// always be a derived prototype. References to a derived prototype's methods are strictly forbidden.
// We should never need to know the actual Device subprototype in this context.
req.device.longPressKeyCode(); // Regardless of the actual Device subprototype, I can be confident this method
// exists for all devices. If I'm worried, there's only one place to check:
// device.js.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment