ACME's main application has a
monolithic design; you can view it in the set of Demo Projects, in the solution named 'Monolith'. Below is the code for the one method of WizbangWidget, 'DoItAll()'. The design embodied in this code presents several problems, both when it comes to testability and problems manifested as violations of design principles. Here is a list of some of those problems.
For the full code, including the company's application that uses WizbangWidget, see the solution Monolith.
Problems
SOLID, DRY, and Loose Coupling Problems | Testability Problems |
- Tightly Coupled with MonolithLogger, MonolithEventListener, MonolithRepository, CoolComponent, and MonolithWebServiceHelper (Control Coupling)
- By having knowledge of and responsibility for instantiating and calling these classes, it violates the Single-Responsibility Principle (a class should have only one responsibility or put another way should have only one reason to change)
- Also, it specifically has knowledge of and responsibility for determining what happens when a Thing is Poked With A Pointed Stick (even though it dispatches to other components for the details of this responsibility, it still has the ultimate responsibility). This violates the Single-Responsibility Prnciple, also.
- If the requirements change so that it should use anything besides any of these components - for instance, if tomorrow it needs to use UncoolComponent to determine the noise a Thing makes - then the internal code will have to change. Thus, it violates the Open/Closed Principle (a class should be Open to extension, but Closed to modification)
- It depends directly on implementations. Thus it violates the Interface Segregation Principle (many client-specific interfaces [are better than one general-purpose interface]), which presumes the use of interface and adherence to the principle "code to an interface, not an implementation" also stated as the Dependency Inversion Principle, see below)
- It depends directly on implementations. Thus it violates the Dependency Inversion Principle ("Depend upon abstractions. Do not depend on concretions.")
- Because of the tight coupling on MonolithEventListener, there's a bug in the application so that the number of events registered keeps getting 'plus-oned' every time you click 'Go'.
| - Very difficult to test that the appropriate events get logged with the appropriate messages
- Very difficult to test that the appropriate events get broadcast
- Very difficult to test with various responses from the Repository
- Very difficult to test that the class logs appropriately if Thing makes a burp noise
- Very difficult to test that the Noise A Thing Makes gets recorded
- Very difficult to test that the Noise A Thing Makes gets recorded
- All of these tests rely on other components 'doing their thing correctly'. Any bugs in these DOC (Depended-On Components) will cause a 'false positive' of the test i.e. it will act as if it caught a defect in the SUT (System Under Test), when in fact the defect is in one of the DOCs
|
And here's that code:
public class WizbangWidget
{
private MonolithLogger _logger = new MonolithLogger ();
public void DoItAll( string someParameter) {
_logger.LogEvent( "Beginning 'DoItAll'");
var eventListener = new MonolithEventListener();
var repository = new MonolithRepository();
var coolComponent = new CoolComponent();
if ( new List< string>{ "1", "2" }.Contains(someParameter)) {
eventListener.SomethingHappened( string.Format( "someParameter = '{0}'", someParameter));
}
else {
eventListener.SomethingHappened( "someParameter was out of bounds.");
_logger.LogEvent( "someParameter was out of bounds." );
}
var thing = repository.GetThing(someParameter);
var doesNotConform = string.Empty;
if (ThingDoesNotConformToTheRulesWeProvideToAddValue(thing)) {
_logger.LogEvent( string.Format( "Thing '{0}' did not conform to our sophisticated rules,", thing.Name));
doesNotConform = " But it did not conform to our sophisticated rules.";
try {
var theNoise = coolComponent.TheNoiseAThingMakesWhenYouPokeItWithAPointedStick(thing);
if (theNoise.Contains( "Burp!!!")) {
_logger.LogEvent( string.Format( "Thing {0} made an inappropriate noise.", thing.Name));
}
var webService = new MonolithWebServiceHelper();
webService.RecordSoundAThingMade(thing, theNoise);
_logger.LogEvent( string.Format( "Thing '{0}' made noise '{1}', and that was recorded." + doesNotConform, thing.Name, theNoise));
eventListener.SomethingHappened( string.Format( "Thing '{0}' was handled.", thing.Name));
}
catch ( Exception ex) {
_logger.LogEvent( string.Format( "ERROR: {0}", ex.Message));
throw
;
}
}
}
}