[TIL] [2024.10.30] Clean Code

김동진김동진
4 min read

💡
QUIZ 01.
// 본인이 가장 잘하는 언어로(JS, Python 등등) 더러운 코드를 깨끗한 코드로 
// 리팩토링하는 예시를 만들어보세요. 현재 파일은 JS 로 되어있지만. 자유롭게 다른 언어로 변경해주세요. 

// 원칙 1. 
단일책임의 원칙

// Before 😣
public class DBConnectionManager {
    private Queue<DBConnectionManager> connectionPool = new LinkedList<>();
    private int MAX_POOL_SIZE;
    private boolean connected = false;

    public DBConnectionManager(int poolSize) {
        this.MAX_POOL_SIZE = poolSize;
        initializeConnections();
    }

    public PooledDataSource(int poolSize) {
        this.MAX_POOL_SIZE = poolSize;
        initialize();
    }

    private void initialize() {
        for (int i = 0; i < MAX_POOL_SIZE; i++) {
            connectionPool.offer(new BasicDBConnection());
        }
    }

    public void connect(){
        if(!connected){
            connected = true;
        }
    }

    public void disconnect() {
        if (connected) {
            connected = false;
        }
    }

    public boolean isConnected() {
        return connected;
    }

    public DBConnection getConnection() {
        connect();
        return connectionPool.poll();
    }

    public void releaseConnection(DBConnection connection) {
        if (connection != null && connectionPool.size() < MAX_POOL_SIZE) {
            connectionPool.offer(connection);
        }
    }

}

// 무엇을 고치려고 하는지, 고치려는 문제가 무엇인지 알려주세요.
커넥션 풀 관리와 개별 커넥션 관리를 DBConnectionManager 에서 모두 담당하고 있다.


// After 😎
// DBConnection.java
public interface DBConnection {
    void connect();
    void disconnect();
    boolean isConnected();
}

public interface DataSource {
    DBConnection getConnection();
    void releaseConnection(DBConnection connection);
}

public class DefaultDBConnection implements DBConnection {
    private boolean connected = false;

    @Override
    public void connect() {
        if (!connected) {
            System.out.println("Connecting to the database...");
            connected = true;
        }
    }

    @Override
    public void disconnect() {
        if (connected) {
            System.out.println("Disconnecting from the database...");
            connected = false;
        }
    }

    @Override
    public boolean isConnected() {
        return connected;
    }
}

public class PooledDataSource implements DataSource {
    private final Queue<DBConnection> connectionPool = new LinkedList<>();
    private final int MAX_POOL_SIZE;

    public PooledDataSource(int poolSize) {
        this.MAX_POOL_SIZE = poolSize;
        initialize();
    }

    private void initialize() {
        for (int i = 0; i < MAX_POOL_SIZE; i++) {
            connectionPool.offer(new DefaultDBConnection());
        }
    }

    @Override
    public DBConnection getConnection() {
        return connectionPool.poll();
    }

    @Override
    public void releaseConnection(DBConnection connection) {
        if (connection != null && connectionPool.size() < MAX_POOL_SIZE) {
            connectionPool.offer(connection);
        }
    }
}
// 어떻게 고쳤는지, 사례에서 무엇을 배워야 하는지 설명해주세요.
개별 커넥션과 커넥션 풀 관리 기능을 분리하고
각각의 책임을 나누어 코드 수정 시 다른 기능에도 영향을 미치지 않도록 격리 함.
💡
QUIZ 02.
// 본인이 가장 잘하는 언어로(JS, Python 등등) 더러운 코드를 깨끗한 코드로 
// 리팩토링하는 예시를 만들어보세요. 현재 파일은 JS 로 되어있지만. 자유롭게 다른 언어로 변경해주세요. 

// 원칙 2. 
예외를 사용하라

// Before 😣
public class UserService {
    private Map<Integer, String> users = Map.of(
        1, "Alice",
        2, "Bob"
    );

    public String findUserById(int userId) {
        return users.get(userId);
    }
}

// 무엇을 고치려고 하는지, 고치려는 문제가 무엇인지 알려주세요.
예외가 발생할 수 있는 상황에서 아무런 조치 없이 값을 반환하고 있다.


// After 😎
public class ApplicationException extends RuntimeException {
    public ApplicationException(String message) {
        super(message);
    }

    public ApplicationException(String message, Throwable cause) {
        super(message, cause);
    }
}

public class UserService {
    private Map<Integer, String> users = Map.of(
        1, "Alice",
        2, "Bob"
    );

    public String findUserById(int userId) {
        if (!users.containsKey(userId)) {
            throw new ApplicationException("해당 유저를 찾지 못했습니다: " + userId + "번 유저");
        }
        return users.get(userId);
    }
}
// 어떻게 고쳤는지, 사례에서 무엇을 배워야 하는지 설명해주세요.
확장성을 고려하여 CustomException(사용자 정의 예외)를 사용하여 정의하고,
예외가 발생할 수 있는 상황을 고려하여 예외 처리하며 에러 메시지를 함께 던진다.
💡
QUIZ 03.
// 본인이 가장 잘하는 언어로(JS, Python 등등) 더러운 코드를 깨끗한 코드로 
// 리팩토링하는 예시를 만들어보세요. 현재 파일은 JS 로 되어있지만. 자유롭게 다른 언어로 변경해주세요. 

// 원칙 3. 
  디미터 법칙

// Before 😣
public class Customer {
    private Address address;

    public Customer(Address address) {
        this.address = address;
    }

    public Address getAddress() {
        return address;
    }
}

public class Address {
    private String city;

    public Address(String city) {
        this.city = city;
    }

    public String getCity() {
        return city;
    }
}

public class Order {
    private Customer customer;

    public Order(Customer customer) {
        this.customer = customer;
    }

    public void printCustomerCity() {
        System.out.println("Customer city: " + customer.getAddress().getCity());
    }
}

// 무엇을 고치려고 하는지, 고치려는 문제가 무엇인지 알려주세요.
결합도가 높아 Customer의 구조가 변경되면 Order도 함께 변경되어야 하며,
Customer의 내부 구조가 노출되고 있다.

// After 😎
public class Customer {
    private Address address;

    public Customer(Address address) {
        this.address = address;
    }

    public String getCity() {
        return address.getCity();
    }
}

public class Address {
    private String city;

    public Address(String city) {
        this.city = city;
    }

    public String getCity() {
        return city;
    }
}

public class Order {
    private Customer customer;

    public Order(Customer customer) {
        this.customer = customer;
    }

    public void printCustomerCity() {
        System.out.println("Customer city: " + customer.getCity());
    }
}


// 어떻게 고쳤는지, 사례에서 무엇을 배워야 하는지 설명해주세요.
디미터 법칙을 적용함으로써 Customer의 구조가 바뀌어도 
Customer가 city를 제공하므로 Order가 변경될 필요가 없어
결합도가 낮아지고 유지보수성이 높아진다.
0
Subscribe to my newsletter

Read articles from 김동진 directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

김동진
김동진