Skip to content
Dev Dump

Design Principles

  1. DRY - Don’t Repeat Yourself
  2. KISS - Keep It Simple and Stupid
  3. YAGNI - You Ain’t Gonna Need It
  4. SoC - Separation of Concerns
  5. Favor Composition over Inheritance
  6. Program to Interface, not Implementation

DRY is a principle aimed at reducing code duplication by ensuring that each piece of knowledge or logic is represented in a single place. This makes the code more maintainable and less prone to errors.

Bad Practice (Violation of DRY):

class Employee {
private String name;
private double salary;
public double calculateBonus() {
return salary * 0.1; // Bonus calculation logic duplicated
}
}
class Manager extends Employee {
public double calculateBonus() {
return getSalary() * 0.1; // Same logic repeated
}
}

Good Practice (Following DRY):

class Employee {
private String name;
private double salary;
public double calculateBonus() {
return salary * 0.1;
}
}
class Manager extends Employee {
// No duplication; reuses the calculateBonus method
}

KISS encourages writing simple, easy-to-understand code rather than overcomplicating logic with unnecessary complexity.

Bad Practice (Violation of KISS):

if (isUserLoggedIn && (userRole.equals("ADMIN") || userRole.equals("MODERATOR")) && isNotBanned) {
grantAccess();
}

Good Practice (Following KISS):

boolean hasAccess = isUserLoggedIn && (isAdmin() || isModerator()) && isNotBanned;
if (hasAccess) {
grantAccess();
}

YAGNI suggests that you should not add functionality until it is actually needed. This helps avoid unnecessary complexity and reduces maintenance overhead.

YAGNI illustration

Bad Practice (Violation of YAGNI):

class User {
private String username;
private String email;
private String phoneNumber; // Added without requirement
private String address; // Added without requirement
}

Good Practice (Following YAGNI):

class User {
private String username;
private String email;
// Only required fields included
}

Separation of Concerns (SoC) means that a software system should be divided into distinct sections, each handling a specific responsibility.

Bad Practice (Violation of SoC):

class UserManager {
public void saveUser(User user) {
// Database logic
}
public void sendEmail(User user) {
// Email sending logic
}
}

Good Practice (Following SoC):

class UserRepository {
public void save(User user) {
// Database logic
}
}
class EmailService {
public void sendEmail(User user) {
// Email sending logic
}
}
  • Description: Prefer combining objects through composition rather than creating class hierarchies through inheritance.
  • Example: Instead of creating a deep inheritance tree of different types of Animal (e.g., Dog, Cat, Bird), use composition to give animals behaviour’s like Speakable, Flyable, Swimmable.
class Animal { }
class Mammal extends Animal { }
class Dog extends Mammal { }
class Cat extends Mammal { }
interface Speakable {
void speak();
}
class Dog implements Speakable {
public void speak() {
System.out.println("Woof!");
}
}

Instead of using inheritance (which creates rigid hierarchies), favor composition to make code more flexible and maintainable.

Composition over Inheritance

Bad Practice (Violation of Composition Over Inheritance):

class Engine {
public void start() {
System.out.println("Engine started");
}
}
class Car extends Engine {
public void drive() {
start();
System.out.println("Car is driving");
}
}

Good Practice (Following Composition Over Inheritance):

class Engine {
public void start() {
System.out.println("Engine started");
}
}
class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void drive() {
engine.start();
System.out.println("Car is driving");
}
}

6. Program to Interface, Not Implementation

Section titled “6. Program to Interface, Not Implementation”
  • Description: Write code that depends on interfaces or abstract classes, not concrete classes.
  • Example: Use an interface like List instead of a concrete class like ArrayList when declaring variables and passing arguments.
    • Instead of: ArrayList<String> myList = new ArrayList<>();
    • Do: List<String> myList = new ArrayList<>();

Bad Practice (Violation of this Principle):

class MySQLDatabase {
public void connect() {
System.out.println("Connecting to MySQL");
}
}
class Application {
private MySQLDatabase database;
public Application(MySQLDatabase database) {
this.database = database;
}
public void start() {
database.connect();
}
}

Good Practice (Following the Principle):

interface Database {
void connect();
}
class MySQLDatabase implements Database {
public void connect() {
System.out.println("Connecting to MySQL");
}
}
class Application {
private Database database;
public Application(Database database) {
this.database = database;
}
public void start() {
database.connect();
}
}