![]() |
Duplication can become annoying and unmanagable just like Carrot Top.   Computer technology is constantly changing and if a product has several different implementations, maintenance will become a nightmare. To see what happens when duplication turns evil click here... |
Duplication can also be avoided in code by:
- DRY - Don't Repeat Yourself
Every piece of knowledge must have one, unambiguous, authoritative, representation within a system.
- Knowledge is never constant
- Code needs to be upgraded frequently.
- Duplicate knowledge in several places is a maintenance nightmare.
One change will require you to find and change all the other places the knowledge is duplicated.
- The DRY principle lessens the total volume of code.
- May take more time to avoid duplication, but it will save you time in the long run.
i.e. Do not copy and paste code.   It may save you time now, but will cost you later.
- The DRY principle should be used in documentation, code, and group work.
- Duplication is NOT a kind of reuse.
Reuse is a good alternative to duplication.   i.e. Inheritance or composition.
- EXCEPTION TO THE RULE
For GUIs, duplication through the copy and paste method is a good practice.
- creating a separate method containing common code.
- creating a superclass if duplication occurs in two separate classes.
Software Maintenance Nightmare
Situation
A software company makes a printing and book-binding machine.   Twenty different businesses own this product.   Each business's printing and binding machine has a different version of the software to run their machine.
When the software company realizes that their software is expressed in twenty different ways, they ask each business if they can standardize the software.   Each business is skeptical.   Their own printing and binding machine is functioning properly and has been for years.   They do not see any reason why the software should be tampered with.   Their mentality is, "If it ain't broke, don't fix it."
A necessary upgrade to the printing and binding machine surfaces.   This machine prints envelopes and a zip code has changed.   Since each machine has different software on it, twenty different, specialized fixes are needed for each of the twenty machines.   Not only is this a waste of time and money, but it could have been avoided years before.
How could this have happened?
Each machine may have started out with the same code.   The code was copied, pasted, and edited for use in all twenty machines.   If any code changes were made, the company had to go get the source code for that particular machine and then make the change.   These changes expanded the code in each machine into its own version.
How could this have been avoided?
Instead of using the "copy and paste" method, which falls under impatient duplication, ACTUAL reuse should have been used, meaning composition and inheritance.   If time had been taken to include these techniques, time and money would have been saved in the long run.
Avoid Magic Numbers
What is a magic number?
A magic number is a numeric constant/numeric literal embedded in code.   This may include 0, 1, or 2 depending upon the language you are using.
Why are magic numbers bad?
How do you get rid of magic numbers?
- The number may change in the future due to new requirements, design changes, or code rewrites.   You will have to change every instance of the number.
- They are not descriptive.
A person reading your code can't immediately determine what the number describes.
- A number could change if moved to another platform.
Define the literal using a descriptive name.   This name is then used in place of the number throughout your program.
These names gives your code advantages:Examples
- If the number changes, the number only has to be altered in one place (where it is defined).
- Your code is more readable.   A descriptive name reveals more than a literal number.
- Eliminate Several Instances of Literal Number
public static final int currentAge = 23;
Every time your age changes, this will be the only place it has to be changed.   This is easier than changing every instance of the numeric literal each time you have a birthday.
- Make the Code More Readable
- Wrong way:
bool apartmentsAvailable = (tenants < 200);
This way 200 is an ambiguous number.
- Correct way:
public static final int NumberOfApartments = 200; ...
bool apartmentsAvailable = (tenants < NumberOfApartments);
Now we can easily see that the tenants variable must be less than the number of Apartments. Also, if the apartment complex expands, then the number of apartments can be easily changed.
- Make Sure the Name is Descriptive
- Wrong way:
for(int i=0; i < 100; i++) {
    ...
}
Here 100 is an ambiguous number.
- Still wrong:
public static final int X = 100;
for(int i=0; i < X; i++) {
    ...
}
This time X is chosen to represent 100, so we can use X in every instance of 100.   The problem is, we still don't know what X represents since the name is not descriptive.
- Correct way:
public static final int NumOfRecords = 100;
for(int i=0; i < NumOfRecords; i++) {
    ...
}
Finally, we can see that the counter must be less than the number of records.   This also gives us some insight into what the loop contains.   It most likely will manipulate each record in some way.
Reuse and duplication each have their advantages and drawbacks.   Reuse is illustrated using three examples: inheritance, composition, and interfaces.   Duplication is represented by the copy and paste practice.
The main issues considered in making the choice between these four options are ownership, trust, and time.
- Ownership
Ownership is how much control you have over your class.
Do other people have access to this class or is it yours alone?
To what extent do others have access?
- Trust
If you trust that the class you want to reuse is reliable, then you will be apt to give up ownership of your own class and trust that the reused class will do the job.
How much do you trust others' classes?
- Time
- Immediate time
Time that is saved right now.
- Long-term time
Time that is saved in the future.
Is saving a little time now more important to you than saving a lot of time in the future?
Copy and Paste | |
|
Copy and pasting a class creates two separate entities of that class.   This way you will have total ownership of both of these classes.   This may be a good thing in some cases since only changes you make will affect your code.   It will become unmanageable since this one change will have to be made in all copied versions of the code.   This links in with the trust issue.   If you have full ownership of this code then you know you can trust it since you have full control over it.   As far as time is concerned, you will save a lot of time immediately.   In the long run, you will lose time.   One change multiplies into how many copies of the code you have floating around. |
Inheritance | |
|
A class using inheritance acquires all the methods of the class it inherits from.   You do not have ownership of the inheriting class since it is the parent class plus its own details.   This "is-a" relationship makes the inheriting class vulnerable to any changes made to the parent class.   This is good in the sense that all changes are made down the inheriting line automatically.   This may not be what you want or may happen unexpectedly.   This is where trust comes in.   Your class relies on the trust of the other classes preceding it in the inheritance tree.   Time may not be saved up front, but in the long run less time will be spent on changes since a change to the parent class will make that change for all classes beneath it. |
Composition | |
|
A class using composition can use other class' methods.   You have more ownership of your class than with inheritance because the relationship is "has-a" rather than "is-a".   Your class "has-a" class that it can borrow methods from.   You still need to trust the class whose methods you have access to, because a change in them can still affect you.   You are not as reliant on this class in composition as you are with inheritance.   In terms of time, you have the advantages of reuse in the long run, but will use a bit more time up front. |
Interfaces | |
|
An interface defines a set of methods but does not implement them.   This essentially calls for better specifications for your code and in Java, each program using this interface is checked to make sure it meets all the criteria of the interface.   Interfaces give you ownership of your code.   Since you have ownership, there is less need to worry about trust.   If your interface has been properly defined, then you should find no trouble trusting it.   Plus, Java won't let anyone compile code that does not follow the interface.   This will take more time up front but save you time later. |
Here is a pdf file for all four reuse and duplication UML diagrams.