J
jehugaleahsa
Don't take this the wrong way but it sounds simple. I'm sure its not tho.
Considering you are having problems it would be advisable to design the app
to the n'th degree, this way anyone coming in especially a code monkey can
just follow design and you then have a something to check their progress
against.
"Plan to throw one away." -- Frederick Brooks. We recently upgraded
the application from C/C++ to C#. The first time we rewrote the code,
it was very nasty C#. I'm talking, redundant SQL, SQL in the code-
behind, DataTables getting used all over the place, resource leaks,
bad UI binding and bugs everywhere. I got tired of living in the
garbage, so I started burrowing my way out. Within the past 3 months,
I have formalize and standardized, refactored and applied design
patterns, etc.
There is practically a document for how to write anything, and its
usually a small document. The problem is really whether that person
will be able to see the system as a whole. If they can see the pieces
of the architecture, they should have no problem keeping the
complexity low. Design patterns aren't as common as architectural
patterns. I mean, we have some code that generates reports using a
builder/iterator/composite pattern and a state pattern for tracking
the state of our data objects, but nothing super complex. Probably the
most complex pattern we use is bridge to work with Crystal Reports.
For instance, my lead developer isn't too keen on modern code design.
The system relies a lot of dependency injection and strategies. When
he is trying to find some code, he gets frustrated because "Go to
Definition" takes him to the interface definition. "I just want to see
the code," he complains.
I would also start designing and implementing common components early and
consider putting these in GAC so the other developers cannot change these
items but develop around them.
Here is the problem I ran into with that. According to Bob Martin's
book, you should try to break up your DLLs such that code that changes
together appear in the same DLL (The Common Closure Principle) and
classes that are used together should be packaged together (Common
Reuse Principle). This means that you should be able to break you code
up into DLLs such that the code inside either form a working unit -or-
that the classes inside the DLL are unlikely to change but are reused
by various other classes in other DLLs.
We have an ICustomer interface that is used by just about every
subsystem. We also use a bunch of other classes, all with their
varying appearances across the subsystems. Then there are some that
only appear here or there. It would be nice to to follow the packaging
principles here, but it would result in an explosion of DLLs. Instead
we have DLLs broken out like this:
Interfaces - This holds the interfaces for the data objects in the
system. It also has code for enumerations, converters, etc.
DataObjects - This holds implementations for the interfaces.
Commands - This holds SQL wrapped in command objects.
Factories - This hold facades that indirectly call on the commands,
allow for sorting, etc.
SubSystem - This is the DLL for a particular sub-system. It is
independent of the input and output.
Application - This calls the subsystem DLL and presents it to the user
or whatever.
With all the tables in the system, the interfaces DLL gets new code
all the time. Since it is at the bottom of the DLL dependency tree,
this forces almost every DLL above it to be recompiled. Eventually,
we'll have all of the interfaces implemented and these changes will
come less often. Which calls on the DataObjects DLL, whose sole
purpose is to provide implementations for the interfaces. Early
investigation into the future needs of the system revealed the need
for separating the data objects from their interfaces. Who knows at
this point?
We do use the GAC, but at this point the DLLs change too often. We are
also a very small shop, so whoever comes in is going to need to work
at all levels of the system. Especially for our new code.
This way you should get a head start before your requirements are fully
realised. basically follow the Agile method in Larman but be more flexible.
Core supporting functionality can be created early or even poached from
other projects, re-use is king.
I agree. Reuse has been the cornerstone of the newest edition of the
system. It might have some performance problems associated with it,
but they are negligible. The reuse has also been the catalyst that
allowed me to standardize so much of the system.
I have a library for Data Startegy, Custom Permission objects for CAS, MSMQ
Provider, Error Handling Startegy, Logging Strategy to name but a few. These
only change where a new project requires slightly different functionality
but then as they are strategies I can creat a new concrete strategy based
around an abstract class and still not affect other projects with the
changes made for the new project, the apps are so specific that it is rare
if ever that the interface would change, whereas apps that do everything are
more likely to have interface chnages which becomes a problem.
I have done this in the past. It takes about 3 tries to find a set of
classes that are resistant to change. I have a bunch of reusable code
sets, too. I think every veteran programmer has a collection of
snippets or libraries. However, in the project, everything has to be
written from scratch. We only write code we need, to prevent code
bloat. We also document and test every line of code that is
implemented. It is really hard to take a piece of code you wrote for a
small web application and make it enterprise ready. I did have some
success doing so on some smaller features, such as a COM library I
wrote for integrating with Novell's GroupWise (email) client. I
experienced the opposite effect with Crystal Reports.
We have a fairly agile approach at my work place, which is pretty
impressive considering it's the gov't. We tend to evolve our code more
often than doing a lot of up-front analysis. We write the code so that
it is easy to refactor later on and we always make the first version
easy to throw away if it totally doesn't work. On the opposite end, we
have a very hard time getting users to test, so our iterations are
short, but not nailed down until a few weeks go by and our users find
issues. We have gotten pretty good at testing in production (yuck) and
working on new requirements at the same time. Fortunately, we have an
awesome automated test setup so most issues are UI-related.
Looking at your design it is also obvious there is potential for your
calculation engine to change so you may want to look at the
strategy/composite patterns for this with the ablility to add new
calculations in the future, but Im sure you have all this in hand.
Our calculation engine is a monster. Essentially, it works like a
batch calculator working on the lines in a report. Each of the lines
holds values that are then added, subtracted, divided or multiplied by
another line's values. Most of the code is the same based on a few
fields from each line, so we have an ILine interface. Then, there is
an abstract Calculator class that takes a list of TLines and then uses
template method to do line-specific operations. Nothing like mixing
template method with generics! There is a calculator for every level
in the calculation. All of the calculation engines rely on the same
Calculator class; they just use it differently (template method).
I've found that the calculation engine isn't something you can explain
in five minutes. There about 12 distinct operations (even two versions
of multiplication). I attribute this to the poor design of the
original system. There is no good reason why the code has to
distinguish between floating point numbers and integers, especially
when a "places" field is provided to specify rounding.
Sometimes it is easy for people to get bogged down in semantics,e.g thats
not a factory its an abstract factory etc etc. Does this really matter?
Nope. Has it added value? YES. Thats what matters.
Absolutely. One thing we're careful not ask for is the name of a
design pattern. We might ask, "What are some design patterns you have
worked with? Give us an example." They some times refer to
architectural patterns, but that's all right. There are about 10
different names for the Observer pattern (events, Listener, etc.). By
having the question we are expecting the programmers we interview to
have some experience with them. It's hard these days to program even
medium-sized projects without running into a couple of them. I don't
think we're eliminating any deserving candidates that way. IMHO.
If the system weren't so large, I wouldn't be so concerned. Our my
coworker (who recently left) came in with a business degree and "kind
of" picked up programming. He was pretty good by the time he left. His
deal was that he didn't want to get into the code, but concentrated
more on the UI. Our system doesn't revolve around the UI and his
resistance resulted in him rushing through the code. He could have
asked me for some guidance but he didn't like to ask questions. It
took him months to implement things that were scheduled to only take a
few days.
Whoever comes into this system really needs to be willing to dedicate
all of their coding skills to it. They have to know how to code! Our
system doesn't require much in the way of complex algorithms, they
when to use what. They need to understand the system and the code that
makes it all work.
Thanks for you input!