Design Patterns Handbook
  • Introduction
  • Creational Patterns
    • Builder
    • Factory
    • Abstract Factory
    • Factory Method
    • Prototype
    • Singleton
    • Object Pool
    • Revealing Constructor
  • Structural Patterns
    • Adapter
    • Composite
    • Proxy
    • Flyweight
    • Facade
    • Bridge
    • Decorator
    • Private Class Data
  • Behavioral Patterns
    • Template Method
    • Mediator
    • Chain Of Responsibility
    • Observer
    • Strategy
    • Command
    • State
    • Visitor
    • Memento
    • Interpreter
    • Null Object
    • Iterator
    • Middleware
  • Clean Code Patterns
    • Extract Method
    • Clarify Responsibility
    • Remove Duplications
    • Keep Refactoring
    • Always Unit Test
    • Create Data Type
    • Comment to Better Name
    • Consistent Naming
    • If-else over ternary operator
    • Composition over Inheritance
    • Too Many Returns
    • Private to Interface
  • Anti Patterns
    • Big Ball of Mud
    • Singleton
    • Mad Scientist
    • Spaghetti Code
    • It Will Never Happen
    • Error Codes
    • Commented Code
    • Abbreviations
    • Prefixes
    • Over Patternized
    • Generic Interface over Function
Powered by GitBook
On this page
  • The problem
  • Solution

Was this helpful?

  1. Clean Code Patterns

Composition over Inheritance

Better to reference other classes using composition than binding code together using inheritance.

The problem

Lets look at crazy example that would over use inheritance.

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/")
public class Controller extends Service {

    @GetMapping("users")
    public ResponseEntity<List<User>> users() {
        return ResponseEntity.ok(getUsers());
    }
}

class Service extends Repository {
    List<User> getUsers() {
        List<String> users = super.findUsers();
        return users.stream().map(User::new).collect(Collectors.toList());
    }
}

class User {
    private final String name;

    User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

class Repository {
    List<String> findUsers() {
        ArrayList<String> users = new ArrayList<>();
        users.add("John");
        return users;
    }
}

Solution

The solution is to use composition. The code becomes more testable.

To make the code really testable, we would probably have to create couple interfaces for the service and repository. That would allow us to mock the code and write proper unit tests.

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/")
public class CompoController {
    private final Service service;

    public CompoController(Service service) {
        this.service = service;
    }

    @GetMapping("users")
    public ResponseEntity<List<User>> users() {
        List<User> users = service.getUsers();
        return ResponseEntity.ok(users);
    }
}

@org.springframework.stereotype.Service
class Service {
    private final Repository repository;

    Service(Repository repository) {
        this.repository = repository;
    }

    List<User> getUsers() {
        List<String> users = repository.findUsers();
        return users.stream().map(User::new).collect(Collectors.toList());
    }
}

class User {
    private final String name;

    User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

@org.springframework.stereotype.Repository
class Repository {
    List<String> findUsers() {
        ArrayList<String> users = new ArrayList<>();
        users.add("John");
        return users;
    }
}
PreviousIf-else over ternary operatorNextToo Many Returns

Last updated 5 years ago

Was this helpful?