Dependency Injection forbids casual use of new
. It favors good old reference passing. It says to put new
as high up the call stack as possible.
Convention over Configuration says the common path should be the easy path. Building the typical version of something shouldn't require much effort.
These ideas seem to be at odds. Is there a way to reconcile them?
For example, can you do better than the following refactoring?
class WelcomePage{
showAboutDialog(){
new AboutDialog().show();
}
}
This violates OCP by not being open to extension. However, this:
class WelcomePage{
AboutDialog aboutDialog;
WelcomePage(AboutDialog aboutDialog)
showAboutDialog(){
aboutDialog.show();
}
}
Violates Convention over Configuration. Yes you've allowed for extension under OCP. But by taking aboutDialog as a parameter you've forced a configuration situation.
Consider:
class WelcomePage{
AboutDialog aboutDialog = new AboutDialog();
WelcomePage(){}
WelcomePage(AboutDialog aboutDialog){
this.aboutDialog = aboutDialog;
}
showAboutDialog(){
aboutDialog.show();
}
}
This follows OCP because now AboutDialog
is an overridable default value. It is open to extension. AboutDialog
is now the convention but it's not required. The disadvantage to the previous version is that AboutDialog
must be deployed with WelcomePage
even if it isn't used. The previous one could avoid dragging an implementation with it by making AboutDialog
fully abstract.
This is not a solution I'd use when crossing a significant boundary into a unit meant to be independently deployed. But I don't think every class boundary must be treated so formally. Sometimes a class boundary exists just because it's handy to have a class.
It seems overridable default values can achieve Convention over Configuration in Pure Dependency Injection style so long as you're not crossing a deployment boundary.
Is there a method that provides more freedom? A consideration I've ignored? Can we do better?
new
" the easy path?