객체 지향 프로그래밍 SOLID 원칙

ilkwon bae·2023년 4월 14일
0
post-thumbnail

객체 지향 프로그래밍에서 SOLID 원칙은 다음과 같습니다.

  • 단일 책임 원칙 (Single Responsibility Principle, SRP)
  • 개방/폐쇄 원칙 (Open/Closed Principle, OCP)
  • 리스코프 치환 원칙 (Liskov Substitution Principle, LSP)
  • 인터페이스 분리 원칙 (Interface Segregation Principle, ISP)
  • 의존 역전 원칙 (Dependency Inversion Principle, DIP)

각 원칙에 대한 코드 예시는 다음과 같습니다.

아래는 Java로 작성된 예시 코드입니다.
1. 단일 책임 원칙 (SRP)

public class User {
    private String name;
    private String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public String getName() {
        return this.name;
    }

    public String getEmail() {
        return this.email;
    }
}

public class UserManager {
    private List<User> users;

    public UserManager(List<User> users) {
        this.users = users;
    }

    public void addUser(User user) {
        this.users.add(user);
    }

    public void removeUser(User user) {
        this.users.remove(user);
    }

    public User getUserByEmail(String email) {
        for (User user : this.users) {
            if (user.getEmail().equals(email)) {
                return user;
            }
        }
        return null;
    }
}

위 코드에서 User 클래스는 사용자의 이름과 이메일을 저장하는 역할만 수행합니다. UserManager 클래스는 사용자를 관리하는 역할을 수행합니다. 이렇게 클래스를 역할에 따라 분리함으로써 각 클래스가 단일 책임을 수행하게 됩니다.

  1. 개방/폐쇄 원칙 (OCP)
public abstract class Shape {
    public abstract void draw();
}

public class Circle extends Shape {
    public void draw() {
        System.out.println("Draw a circle");
    }
}

public class Square extends Shape {
    public void draw() {
        System.out.println("Draw a square");
    }
}

public class Drawer {
    public void drawShape(Shape shape) {
        shape.draw();
    }
}
코드를 입력하세요

위 코드에서 Shape 클래스는 도형을 나타내는 추상 클래스입니다. Circle 클래스와 Square 클래스는 Shape 클래스를 상속하며 각 도형을 그리는 draw 메서드를 구현합니다. Drawer 클래스는 Shape 객체를 인자로 받아 해당 도형을 그리는 drawShape 메서드를 가지고 있습니다. 이렇게 도형을 나타내는 클래스와 그리는 클래스를 분리함으로써, 새로운 도형을 추가해도 Drawer 클래스를 수정하지 않아도 되는 개방/폐쇄 원칙을 지키게 됩니다.

  1. 리스코프 치환 원칙 (LSP)
public interface Vehicle {
    public void start();
    public void stop();
}

public class Car implements Vehicle {
    public void start() {
        System.out.println("Start the car");
    }

    public void stop() {
        System.out.println("Stop the car");
    }
}

public class Motorbike implements Vehicle {
    public void start() {
        System.out.println("Start the motorbike");
    }

    public void stop() {
        System.out.println("Stop the motorbike");
    }
}

public class VehicleController {
    private Vehicle vehicle;

    public VehicleController(Vehicle vehicle) {
        this.vehicle = vehicle;
    }

    public void startVehicle() {
        this.vehicle.start();
    }

    public void stopVehicle() {
        this.vehicle.stop();
    }
}
코드를 입력하세요

위 코드에서 Vehicle 인터페이스는 start와 stop 메서드를 가지고 있습니다. Car 클래스와 Motorbike 클래스는 Vehicle 인터페이스를 구현하며 각각의 차량에 맞게 start와 stop 메서드를 구현합니다. VehicleController 클래스는 Vehicle 객체를 인자로 받아 차량을 조종하는 startVehicle와 stopVehicle 메서드를 가지고 있습니다.

이렇게 인터페이스를 이용해 다형성을 구현하면 VehicleController 클래스가 Car 클래스나 Motorbike 클래스를 구체적으로 알 필요가 없으며, 이 클래스를 확장해 새로운 차종을 추가하더라도 VehicleController 클래스를 수정할 필요가 없어 리스코프 치환 원칙을 지킬 수 있습니다.

  1. 인터페이스 분리 원칙 (ISP)
public interface Readable {
    public void read();
}

public interface Writable {
    public void write();
}

public interface Printable {
    public void print();
}

public class Printer implements Printable {
    public void print() {
        System.out.println("Print the document");
    }
}

public class Scanner implements Readable {
    public void read() {
        System.out.println("Scan the document");
    }
}

public class FaxMachine implements Readable, Writable, Printable {
    public void read() {
        System.out.println("Scan the document and read it");
    }

    public void write() {
        System.out.println("Write the document and send it");
    }

    public void print() {
        System.out.println("Print the received document");
    }
}
코드를 입력하세요

위 코드에서 Readable 인터페이스는 read 메서드를 가지며 Writable 인터페이스는 write 메서드를 가집니다. Printable 인터페이스는 print 메서드를 가집니다. Printer 클래스는 Printable 인터페이스를 구현하며 print 메서드를 구현합니다. Scanner 클래스는 Readable 인터페이스를 구현하며 read 메서드를 구현합니다. FaxMachine 클래스는 Readable, Writable, Printable 인터페이스를 모두 구현하며 각각의 메서드를 구현합니다.

이렇게 인터페이스를 분리함으로써 각각의 클래스는 필요한 인터페이스만 구현하면 됩니다. 예를 들어 Printer 클래스는 출력(print) 기능만을 제공하기 때문에 Printable 인터페이스만 구현하면 됩니다. Scanner 클래스는 스캔(read) 기능만을 제공하기 때문에 Readable 인터페이스만 구현하면 됩니다. 또한 FaxMachine 클래스는 스캔, 출력, 전송 기능을 모두 제공하기 때문에 각각의 인터페이스를 구현합니다. 이렇게 하면 각각의 클래스는 자신이 필요로 하는 기능만 구현하므로 인터페이스 분리 원칙을 지킬 수 있습니다.

  1. 의존 역전 원칙 (DIP)
public interface DataSource {
    public void writeData(String data);
}

public class FileDataSource implements DataSource {
    public void writeData(String data) {
        System.out.println("Write data to a file: " + data);
    }
}

public class DatabaseDataSource implements DataSource {
    public void writeData(String data) {
        System.out.println("Write data to a database: " + data);
    }
}

public class DataWriter {
    private DataSource dataSource;

    public DataWriter(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void write(String data) {
        this.dataSource.writeData(data);
    }
}
코드를 입력하세요

위 코드에서 DataSource 인터페이스는 writeData 메서드를 가지며, FileDataSource 클래스와 DatabaseDataSource 클래스는 DataSource 인터페이스를 구현하며 writeData 메서드를 구현합니다. DataWriter 클래스는 DataSource 객체를 인자로 받아 데이터를 저장하는 write 메서드를 가집니다.

이렇게 의존 역전 원칙을 지키면 DataWriter 클래스는 DataSource 객체의 구체적인 종류를 알 필요가 없습니다. 즉, FileDataSource 클래스나 DatabaseDataSource 클래스 중 어떤 것을 사용하더라도 DataWriter 클래스는 수정할 필요가 없습니다. 이렇게 하면 클래스 간의 결합도가 낮아져 유연한 코드를 작성할 수 있습니다.

profile
좋은 개발자가 되고 싶은 그냥 개발자

0개의 댓글