Skip to content

Instantly share code, notes, and snippets.

@Integralist
Last active August 29, 2015 13:57
Show Gist options
  • Save Integralist/9887248 to your computer and use it in GitHub Desktop.
Save Integralist/9887248 to your computer and use it in GitHub Desktop.
AOP (Aspect-Oriented Programming)

What is AOP?

Aspect Oriented Programming is a means to change the behaviour of – or add behaviour to – methods and functions (including constructors) non-invasively. The added behaviour is called “advice” and can be added before, after, or around the function it advises.

This description is similar to the Extract Surrounding refactoring method. The difference is in the direction of the change. It seems AOP is more focused at modifying existing behaviour non-invasively; where as the Extract Surrounding Method actually changes the source code to allow this type of behavioural modification.

Libraries

// Variation 1 (AOP format -> modifies behaviour without changing the `foo` method code)
var obj = {
foo: function(a, b, c) {
console.log('foo', a, b, c);
}
};
var originalMethod = obj.foo;
originalMethod(1, 2, 3) // => foo, a, b, c
obj.foo = function(a, b, c) {
console.log('before');
originalMethod.apply(this, arguments)
console.log('after');
};
obj.foo(1, 2, 3) // => before; foo, a, b, c; after
// Variation 2 (Same as the first but abstracted into reusable helper functions)
function before(f, advice) {
return function () {
advice.apply(this, arguments);
return f.apply(this, arguments);
};
}
function after(f, advice) {
return function () {
var result = f.apply(this, arguments);
advice.call(this, result);
return result;
};
}
var obj = {
foo: function(a, b, c) {
console.log('foo', a, b, c);
}
};
obj.foo = before(obj.foo, function(){
console.log('Before!!');
});
obj.foo = after(obj.foo, function(){
console.log('After!!');
});
obj.foo(1, 2, 3) // => Before!!; foo, a, b, c; After!!
// Variation 3 ("Extract Surrounding" format -> not AOP as it modifies the source `foo` method)
var obj = {
foo: function(before, after) {
if (before) before();
console.log('foo');
if (after) after();
}
};
function before(){
console.log('before');
}
function after(){
console.log('after');
}
object.foo(before, after);
// Different example of the second variation
function logsArguments (fn) {
return function () {
console.log.apply(this, arguments);
return fn.apply(this, arguments)
}
}
function sum (a, b) {
return a + b;
}
var logsSum = logsArguments(sum);
console.log(
logsSum(1, 3)
);
module AOP
def around(fn_name)
old_method = instance_method(fn_name)
define_method(fn_name) do |*args|
yield :before, args if block_given?
old_method.bind(self).call *args
yield :after, args if block_given?
end
end
end
class Foo
extend AOP
def process(msg)
puts msg
end
end
Foo.around('process') do |state, args|
if state == :before
puts 'Blah 1'
elsif state == :after
puts 'Blah 2'
end
end
Foo.new.process('hai')
module Around
def around(fn_name)
old_method = instance_method(fn_name)
define_method(fn_name) do |*args|
puts "before"
old_method.bind(self).call *args
puts "after"
end
end
end
class Foo
extend Around
def bar
puts "Bar!"
end
bar = around(:bar)
end
Foo.new.bar # => before; Bar!; after
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment