In OOP we use polymorphism so an abstraction can have multiple implementations. Let's look at the following example:
//trains abstraction
public interface Train
{
move();
}
public class MonoRail:Train
{
public override move()
{
//use one track;
}
}
public class Rail:Train
{
public override move()
{
//use two tracks;
}
}
A new requirement introduced and needs to bring in the acceleration perspective of the trains, so change the code as below.
public interface Train
{
void move();
}
public class MonoRail:Train
{
public override void move()
{
//use one track;
}
}
public class ElectricMonoRail:MonoRail
{
public override void move()
{
//use electric engine on one track.
}
}
public class DieselMonoRail: MonoRail
{
public override void move()
{
//use diesel engine on one track.
}
}
public class Rail:Train
{
public override void move()
{
//use two tracks;
}
}
public class ElectricRail:Rail
{
public override void move()
{
//use electric engine on two tracks.
}
}
public class DieselRail: Rail
{
public override void move()
{
//use diesel engine on two tracks.
}
}
The above code is not maintainable and lacks reusability (assuming we could reuse the acceleration mechanism for the same track platform). The following code applies the bridge pattern and separates the two different abstractions, train transport and acceleration.
public interface Train
{
void move(Accelerable engine);
}
public interface Accelerable
{
public void accelerate();
}
public class MonoRail:Train
{
public override void move(Accelerable engine)
{
//use one track;
engine.accelerate(); //engine is pluggable (runtime dynamic)
}
}
public class Rail:Train
{
public override void move(Accelerable engine)
{
//use two tracks;
engine.accelerate(); //engine is pluggable (runtime dynamic)
}
}
public class ElectricEngine:Accelerable{/*implementation code for accelerable*/}
public class DieselEngine:Accelerable{/*implementation code for accelerable*/}
If you're measuring methods then this is a classic example of cross-cutting concerns that AOP is designed to solve.
My Java is weak, but something like this in C# using PostSharp would work...
Create a ProfileAttribute
class that does the profiling and logs it to some log file/database/whatever
public sealed class ProfileAttribute : Attribute
{
private StopWatch stopWatch;
public override OnEntry(MethodExecutionArgs args)
{
this.stopWatch = new StopWatch();
this.stopWatch.Start();
}
public override OnExit(MethodExecutionArgs args)
{
this.stopWatch.Stop();
/*
log(string.Format("[{0}]{1}.{2} took {3}ms",
DateTime.Now,
args.ClassName, // may not be actual member name, I forget
args.MethodName, // may not be actual member name, I forget
stopWatch.Elapsed.TotalMilliseconds));
*/
}
}
Then, you can attribute any method that you want to profile (without having to clutter up the code too much) just by applying the attribute like so:
[Profile]
public void WatchedMethod()
{
/* Code */
}
Afterwards, you can check your log(s) and should see something like:
[Jan 1, 2012 00:00:01]MyClass.WatchedMethod took 1.001ms
[Jan 1, 2012 00:00:02]MyClass.WatchedMethod took 1.000ms
[Jan 1, 2012 00:00:03]MyClass.WatchedMethod took 1.002ms
edits
At which point... it makes sense to write a small utility that can pull the data from logs, filter it, and present it in such a way that is usable to you (average the running times, show outlier times, etc.)
Also note that using certain AOP frameworks (maybe AspectJ... not sure) you can also decide what build(s) certain aspects can be built into... ie. you can weave this profile attribute into the debug build, but not weave it into the release build.
Note about runtime performance:
If your weaver is a compile-time weaver, it will inject the aspect's code into your code during the build. Using PostSharp with the example given, the resulting code will be:
private StopWatch stopWatch;
public void WatchedMethod()
{
this.stopWatch = new StopWatch();
this.stopWatch.Start();
/* Code */
this.stopWatch.Stop();
/*
log(string.Format("[{0}]{1}.{2} took {3}ms",
DateTime.Now,
args.ClassName, // may not be actual member name, I forget
args.MethodName, // may not be actual member name, I forget
stopWatch.Elapsed.TotalMilliseconds));
*/
}
Best Answer
A key misconception in today's coding world is that patterns are building blocks. You take an
AbstractFactory
here and aFlyweight
there and maybe aSingleton
over there and connect them together with XML and presto, you've got a working application.They're not.
Hmm, that wasn't big enough.
Patterns are not building blocks
That's better.
A pattern is something that you use when you find that you've got a problem - you need some flexibility that the pattern provides, or that you've stumbled across when you are making a little language in the config file and you say "wait a moment, stop, this is its own interpreter that I'm writing — this is a known and solved problem, use an Interpreter pattern."
But note there, that it's something that you discover in your code, not something you start out with. The creators of Java didn't say "Oh, we'll put a Flyweight in the Integer" at the start, but rather realized a performance issue that could be solved by a flyweight.
And thus, there's no "flow chart" that you use to find the right pattern. The pattern is a solution to a specific type of problem that has been encountered again and again and the key parts of it distilled into a Pattern.
Starting out with the Pattern is like having a solution and looking for a problem. This is a bad thing: it leads to over engineering and ultimately inflexibility in design.
As you are writing code, when you realize that you're writing a Factory, you can say "ah ha! that's a factory I'm about to write" and use your knowledge of knowing the Factory pattern to rapidly write the next bit of code without trying to rediscover the Factory pattern. But you don't start out with "I've got a class here, I'll write a factory for it so that it can be flexible" — because it won't.
Here's an excerpt from an interview with Erich Gamma (of Gamma, Helm, Johnson, and Vissides): How to Use Design Patterns:
The best help for the "what to use, when" is likely the Wikipedia page for software design pattern - the "Classification and list" section describes the category each pattern is in and what it does. There's no flowchart; the description there is probably the best you'll find as a short snippet for "what to use, when."
Note that you'll find different patterns in different areas of programming. Web design has its own set of patterns while JEE (not web design) has another set of patterns. The patterns for financial programming are completely different to those for stand alone application UI design.
So any attempt to list them all is inherently incomplete. You find one, figure out how to use it and then it eventually becomes second nature and you don't need to think about how or when to use it ever again (until someone asks you to explain it).