return rules;
}
}
class Net extends AbstractNet {
private Bus bus;
Set assignedRules() {
Set result = new HashSet();
result.addAll(super.assignedRules());
result.addAll(bus.assignedRules());
return result;
}
}
Of course, there would be a great deal of supporting code, but this covers
the basic functionality of
the script.
The application requires import/export logic, which we'll encapsulate into some simple services.
Service
Responsibility
Net List import
Reads Net List file, creates instance of Net for each entry
Net Rule export
Given
a collection of Nets, writes all attached rules into the Rules File
We'll also need a few utilities:
Class
Responsibility
Net Repository
Provides access to Nets by name
Inferred Bus
Factory
Given a collection of Nets, uses naming
conventions to infer Buses, creates
instances
Bus Repository
Provides access to Buses by name
Now, starting the application is a matter of initializing the repositories with imported data:
Collection nets = NetListImportService.read(aFile);
NetRepository.addAll(nets);
Collection buses = InferredBusFactory.groupIntoBuses(nets);
BusRepository.addAll(buses);
Each of the services and repositories can be unit-tested. Even more important,
the core domain
logic can be tested. Here is a unit test of the most central behavior (using the JUnit test
framework):
public void testBusRuleAssignment() {
Net a0 = new Net("a0");
Net a1 = new Net("a1");
Bus a = new Bus("a"); //Bus is not conceptually dependent
a.addNet(a0); //on name-based recognition, and so
a.addNet(a1); //its tests should not be either.
NetRule minWidth4 = NetRule.create(MIN_WIDTH, 4);
a.assignRule(minWidth4);
assertTrue(a0.assignedRules().contains(minWidth4));
assertEquals(minWidth4, a0.getRule(MIN_WIDTH));
assertEquals(minWidth4, a1.getRule(MIN_WIDTH));
}
An interactive user interface
could present a list of buses, allowing the user to assign rules to each,
or it could read from a file of rules for backward compatibility. A façade makes access simple for
either interface. Its implementation echoes the test:
public void assignBusRule(String busName, String ruleType,
double parameter){
Bus bus = BusRepository.getByName(busName);
bus.assignRule(NetRule.create(ruleType, parameter));
}
Finishing:
NetRuleExport.write(aFileName, NetRepository.allNets());
(The service asks each
Net
for
assignedRules()
, and then writes them fully expanded.)
Of course, if there were only one operation (as in the example), the
script-based approach might
be just as practical. But in reality, there were 20 or more. The
MODEL-DRIVEN DESIGN
scales easily
and can include constraints on combining rules and other enhancements.
The second design also accommodates testing. Its components have well-defined interfaces that
can be unit-tested. The only way to test the script is to do an end-to-end file-in/file-out
comparison.
Keep in mind that such a design does not emerge in a single step. It would take several iterations
of refactoring and knowledge crunching to distill the important concepts
of the domain into a
simple, incisive model.
[ Team LiB ]