Before moving into the DRY principle, let's understand the goals of a good programmer:
- Solving Problems
- Writing Amazing Code
- Maintainsability
- Simplicity
- Cleanliness
- Optimization
Why write clean, simple, and maintainable code?
You're writing for humans.
- Whether it's for a team.
- When you revisit your code 6 months later, it must be understandable by you.
Is there a standard way to write clean code?
Yes! Here are some principles:
- DRY
- KISS
- YAGNI
- SOLID
The DRY principle was introduced by Andy Hunt and Dave Thomas in their book "The Pragmatic Programmer".
Let's Deep Dive...
In this article, we will cover:
What is DRY?
"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system."
In simple terms, Don't Repeat Yourself (DRY) means we should avoid duplicating code, logic, or knowledge. Instead, put it in one place and reuse it!
The DRY principle encourages:
- Modular code
- Reusable code
- Less repetitive code
Example Codes: Non-DRY vs DRY
Example 1: Avoiding Code Duplication (JavaScript)
Non-DRY:
function calculateElectronicTax(price) {
return price * 0.18;
}
function calculateGroceryTax(price) {
return price * 0.05;
}
function calculateClothingTax(price) {
return price * 0.12;
}
- We've written separate functions for each type of tax calculation, even though the logic is the same. Imagine doing this for 50 types - it gets messy!
DRY:
function calculateTax(price, taxRate) {
return price * taxRate;
}
// Usage
const electronicsTax = calculateTax(1000, 0.18);
const groceryTax = calculateTax(500, 0.05);
const clothingTax = calculateTax(800, 0.12);
- One reusable function - clean and scalable!
Example 2: Use Functions (C++)
Non-DRY:
void swapNumbersNonDry() {
int a = 5, b = 10;
int temp = a;
a = b;
b = temp;
int x = 20, y = 30;
temp = x;
x = y;
y = temp;
}
- The swap logic is copied and pasted - not efficient!
DRY:
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int num1 = 5, num2 = 10;
swap(num1, num2);
}
- One reusable
swap()function saves us time and effort.
Example 3: Object-Oriented Approach (C++)
Non-DRY:
#include <iostream>
using namespace std;
void submitButton() {
cout << "Form submitted." << endl;
}
void cancelButton() {
cout << "Action canceled." << endl;
}
int main() {
submitButton();
cancelButton();
}
- Separate functions even though they perform similar tasks.
DRY (Using Classes):
#include <iostream>
using namespace std;
class Button {
public:
virtual void onClick() = 0; // Abstract method
};
class SubmitButton : public Button {
public:
void onClick() override {
cout << "Form submitted." << endl;
}
};
class CancelButton : public Button {
public:
void onClick() override {
cout << "Action canceled." << endl;
}
};
int main() {
SubmitButton submit;
submit.onClick();
CancelButton cancel;
cancel.onClick();
}
- The structure is reusable - adding new buttons becomes easier!
Example 4: Create Reusable Components (ReactJS)
Non-DRY:
function Header() {
return <h1>My Website</h1>;
}
function Footer() {
return <h1>My Website</h1>;
}
- Same title repeated - not efficient.
DRY:
function Title({ text }) {
return <h1>{text}</h1>;
}
function Header() {
return <Title text="My Website" />;
}
function Footer() {
return <Title text="My Website" />;
}
- One reusable
Titlecomponent - clean and flexible!
Example 5: Use Constants or Config Files
Non-DRY:
console.log("Connecting to http://example.com");
- Imagine having to update this URL in 50 places โ it becomes complicated!
DRY:
// .env file
BASE_URL=http://example.com
// usage
console.log(`Connecting to ${process.env.BASE_URL}`);
- Change the URL once in the
.envfile instead of 50 places!
Why is it Important?
- Efficiency: Reduces code size, saves time, and eliminates redundancy.
- Maintainability: Updates and debugging are simpler with centralized changes.
- Scalability: Modular code makes it easier to add new features.
- Consistency: Ensures uniform behavior and reduces bugs.
- Faster Development: Reusing code accelerates the development process.
How to Apply it?
- Identify Repetitive Code: Look for patterns where the same logic appears again and again.
- Extract Common Functionality: Move repeated code into functions, classes, or modules.
- Use Inheritance and Composition: Create a hierarchy of classes or compose objects to avoid code duplication.
- Leverage Libraries and Frameworks: Use libraries to save time, but avoid overuse.
- Refactor Regularly: Keep reviewing and refactoring to remove duplication.
When Not to Use it?
Sometimes DRY is not the best choice:
- Premature Abstraction: Donโt make things too reusable before you need them.
- Performance-critical code: Duplication may improve performance in certain cases.
- One-time usage: Abstracting code used once is unnecessary and time-consuming.
- Readability issues: Over-abstracting can lead to confusing and harder-to-read code.
- Legacy or Temporary code: Abstraction may not be worth it for code that is short-lived.
- Debugging: Duplicated code can sometimes be easier to debug.
Disadvantages of DRY
- Over-Abstraction: Making everything reusable too early can hurt readability and understanding.
- Takes Time: Implementing DRY requires careful planning and time upfront.
- Misuse: Incorrect application of DRY can create tightly coupled, hard-to-maintain code.
- Hard Refactoring: DRY is difficult to apply in messy codebases and may complicate refactoring.
DRY (Don't Repeat Yourself) makes code readable, maintainable, and efficient.
However, use it wisely โ too much abstraction is harmful. DRY works best when combined with good practices like SOLID principles.
By applying DRY thoughtfully, we create better, cleaner, and smarter software!
๐ฌ Did you find this useful?
If this article helped you, feel free to like and share!
Have any questions or ideas?
Leave a comment below โ I'd love to hear from you! ๐
Top comments (0)