>  기사  >  Java  >  견고한 원칙

견고한 원칙

Linda Hamilton
Linda Hamilton원래의
2024-10-21 06:12:02898검색

SOLID Principles

SOLID는 객체 지향 프로그래밍(OOP)에서 코드의 관리 효율성과 확장성을 향상시키기 위해 설계된 기본 원칙 모음입니다. 이는 5가지 핵심 원칙으로 구성됩니다.

  1. S 단일 책임 원칙 — SRP
  2. O 펜 폐쇄 원칙 — OCP
  3. L iskov의 대체 원리 — LSP
  4. I 인터페이스 분리 원칙 — ISP
  5. D 종속성 반전 원리 — DIP

이러한 원칙은 로버트 C. 마틴(밥 삼촌이라고도 함)이 2000년대 초반에 도입한 이후 소프트웨어 개발 커뮤니티에서 널리 채택되었습니다. SOLID 원칙을 따르면 개발자는 더 쉽게 이해하고, 수정하고, 확장할 수 있는 코드를 생성하여 더욱 강력하고 유지 관리가 가능한 소프트웨어 시스템을 만들 수 있습니다.

단일 책임 원칙(SRP)

단일 책임 원칙은 OOP와 SOLID의 첫 번째이자 가장 기본적인 원칙입니다. 이름에서 알 수 있듯이 이 원칙은 “하나의 클래스는 하나의 구체적인 책임만 맡아야 한다”는 의미입니다.

invoice라는 클래스가 있다고 가정해 보겠습니다. 여기에는 generateInvoice() 및 saveToFiles() 메소드 2개가 포함되어 있습니다.

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}

Invoice 클래스에는 두 가지 책임이 있으므로 이는 좋은 습관이 아닙니다. 더 나은 접근 방식은 이러한 기능을 전용 클래스로 분리하는 것입니다.

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}

여기서 사용 사례에 대한 2개의 클래스가 있음을 볼 수 있습니다.

  • 인보이스 생성
  • 파일로 저장

SRP를 따를 때의 이점

  • 향상된 코드 구성: 문제를 여러 클래스로 분리하면 코드베이스가 더욱 체계화되고 탐색하기 쉬워집니다.
  • 유지관리성 향상 : 클래스에 단일 책임이 있으면 해당 클래스의 목적을 이해하고 의도하지 않은 부작용 없이 변경하기가 더 쉽습니다.
  • 재사용성 증가: 단일 책임이 있는 클래스는 애플리케이션의 다른 부분이나 심지어 다른 프로젝트에서도 재사용할 가능성이 더 높습니다.
  • 더 쉬운 테스트 : 단일 책임이 있는 클래스는 일반적으로 더 작고 더 집중적이므로 독립적으로 테스트하기가 더 쉽습니다.

개방-폐쇄 원칙(OCP)

개방-폐쇄 원칙은 SOLID의 또 다른 핵심 원칙입니다. 이 원칙은 1997년 Bertrand Meyer에 의해 도입되었습니다. 이 원칙의 배경이 되는 아이디어는 "소프트웨어 아티팩트(클래스, 모듈 및 기능)는 확장을 위해 열려야 하지만 수정을 위해서는 닫혀 있어야 합니다."

예를 들어

Shape라는 클래스가 있다고 가정해 보겠습니다. 이 클래스를 사용하여 도형의 면적을 계산할 수 있습니다.

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}

위 코드에서 새 모양을 추가하려면 기존 Shape 클래스를 수정해야 하는데 이는 좋은 방법으로 간주되지 않습니다.

아래는 개방-폐쇄 원칙을 이 시나리오에 적용하는 방법을 보여주는 코드 예시입니다.

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}

OCP를 적용하면 현재 구현을 수정하지 않고도 원하는 대로 많은 모양을 추가할 수 있습니다.

참고: 인터페이스를 사용하는 것이 OCP를 달성하는 유일한 방법은 아닙니다.

OCP를 따를 때의 이점

  • 버그 위험 감소 : 기존 코드를 수정하지 않음으로써 새로운 버그가 발생하거나 기존 기능이 손상될 위험이 최소화됩니다.
  • 유지관리성 향상 : OCP를 따르는 코드는 기존 코드베이스를 변경하지 않고도 새로운 기능을 추가할 수 있으므로 유지 관리 및 확장이 더 쉽습니다.
  • 향상된 유연성: 추상화와 다형성을 사용하면 더욱 유연하고 적응 가능한 디자인이 가능해 변화하는 요구 사항을 더 쉽게 수용할 수 있습니다.

Liskov의 대체 원리(LSP)

Liskov의 대체 원칙은 OOP의 또 다른 중요한 원칙입니다. 이는 1987년 데이터 추상화에 관한 컨퍼런스 강연에서 Barbara Liskov에 의해 소개되었습니다.

이 원칙은 "슈퍼클래스의 객체는 프로그램의 정확성을 변경하지 않고 하위 클래스의 객체로 대체 가능해야 한다"는 것입니다.

예를 들어 Circle과 Rectangle이 Shape의 하위 유형인 경우 Shape 개체를 문제 없이 Circle 또는 Rectangle 개체로 바꿀 수 있습니다.

public class Shape {
    private String shapeType;
    private double radius;
    private double length;
    private double width;

    public Shape(String shapeType, double radius, double length, double width) {
        this.shapeType = shapeType;
        this.radius = radius;
        this.length = length;
        this.width = width;
    }

    public double area() {
        if (shapeType.equals("circle")) {
            return Math.PI * (radius * radius);
        } else if (shapeType.equals("rectangle")) {
            return length * width;
        } else {
            throw new IllegalArgumentException("Unknown shape type");
        }
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape("circle", 5, 0, 0);
        Shape rectangle = new Shape("rectangle", 0, 4, 6);

        System.out.println(circle.area());
        System.out.println(rectangle.area());
    }
}

이 예에서 알 수 있듯이 Liskov 대체 원칙을 준수한다는 것은 슈퍼클래스 인스턴스를 하위 클래스 인스턴스로 원활하게 대체할 수 있어야 함을 의미합니다.

LSP를 따를 때의 이점

  • 향상된 코드 재사용성: 기본 유형을 하위 유형으로 대체할 수 있도록 함으로써 기본 유형을 사용하는 코드는 모든 하위 유형과도 작동할 수 있어 코드 재사용이 촉진됩니다.
  • 향상된 유지 관리 : LSP를 따르는 코드는 코드베이스를 수정하거나 확장할 때 버그가 발생할 위험이 줄어들기 때문에 유지 관리가 더 쉽습니다.
  • 더 나은 테스트 용이성: LSP를 사용하면 클래스와 해당 하위 유형에 대한 단위 테스트를 더 쉽게 작성할 수 있습니다. 테스트는 기본 유형에 대해 작성될 수 있고 모든 하위 유형에 대해 작동해야 하기 때문입니다.

인터페이스 분리 원칙(ISP)

인터페이스 분리 원칙은 로버트 C. 마틴이 소개한 5가지 SOLID 원칙 중 하나입니다. "클라이언트가 사용하지 않는 인터페이스에 의존하도록 강요해서는 안 됩니다."라고 명시되어 있습니다.

즉, 하나의 범용 인터페이스를 사용하는 것보다 여러 작업별 인터페이스를 사용하는 것이 더 좋습니다.

아래 예시는 범용 인터페이스의 사용법을 보여줍니다.

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}

MultifunctionPrinter와 같은 범용 인터페이스를 사용하면 불필요한 메서드를 구현하게 되는데, 이는 나쁜 습관으로 간주됩니다. 이 시나리오에 인터페이스 분리 원칙을 적용할 수 있는 방법을 살펴보겠습니다.

인터페이스

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}

구현

public class Shape {
    private String shapeType;
    private double radius;
    private double length;
    private double width;

    public Shape(String shapeType, double radius, double length, double width) {
        this.shapeType = shapeType;
        this.radius = radius;
        this.length = length;
        this.width = width;
    }

    public double area() {
        if (shapeType.equals("circle")) {
            return Math.PI * (radius * radius);
        } else if (shapeType.equals("rectangle")) {
            return length * width;
        } else {
            throw new IllegalArgumentException("Unknown shape type");
        }
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        Shape circle = new Shape("circle", 5, 0, 0);
        Shape rectangle = new Shape("rectangle", 0, 4, 6);

        System.out.println(circle.area());
        System.out.println(rectangle.area());
    }
}

ISP를 적용하여 프린터, 스캐너, 팩스와 같은 더 작은 역할별 인터페이스 로 분할했습니다. 이를 통해 각 클래스(예: BasicPrinter, AdvancedPrinter 또는 FaxMachine)는 관련 기능만 구현하여 모듈성을 촉진하고 불필요한 종속성을 줄일 수 있습니다.

ISP를 따를 때의 이점

  • 모듈식 및 재사용 가능한 코드 : 대규모 인터페이스를 더 작고 구체적인 인터페이스로 분할함으로써 코드는 더욱 모듈화되고 재사용 가능해집니다. 클래스나 모듈은 필요한 인터페이스만 구현할 수 있으므로 불필요한 종속성을 줄이고 시스템의 여러 부분에서 코드를 더 쉽게 재사용할 수 있습니다.
  • 코드 복잡성 감소: 클래스나 모듈이 필요한 인터페이스에만 의존하면 코드가 덜 복잡해지고 이해하기 쉬워집니다. 개발자가 불필요한 메서드나 종속성을 처리할 필요가 없기 때문입니다. 이는 특정 사용 사례와 관련이 없습니다.
  • 유지관리성 향상 : 더 작고 집중된 인터페이스로 코드 유지 관리가 더 쉬워집니다. 하나의 인터페이스를 변경해도 시스템의 다른 부분에 영향을 미칠 가능성이 낮아 버그가 발생하거나 기존 기능이 손상될 위험이 줄어듭니다.
  • 더 나은 테스트 가능성: 더 작고 집중된 인터페이스를 통해 개별 구성 요소에 대한 단위 테스트를 더 쉽게 작성할 수 있습니다. 관련 없는 메서드나 종속성에 영향을 받지 않고 특정 동작에만 테스트를 집중할 수 있기 때문입니다.
  • 유연성 증가 : ISP를 준수함으로써 시스템은 더욱 유연해지고 확장이나 수정이 더 쉬워집니다. 전체 시스템에 영향을 주지 않고 새로운 인터페이스를 만들거나 기존 인터페이스를 수정하여 새로운 기능이나 요구 사항을 추가할 수 있습니다.

종속성 역전 원리(DIP)

의존성 역전 원칙은 SOLID의 최종 원칙입니다. 로버트 C. 마틴이 소개한 내용이기도 합니다. 이는 느슨하게 결합된 코드를 촉진합니다.

DIP에는 몇 가지 사항이 명시되어 있습니다.

  • 상위 모듈은 하위 모듈에 종속되어서는 안 됩니다.
  • 둘 다 추상화에 의존해야 합니다.
  • 추상은 세부사항에 의존해서는 안 됩니다.
  • 세부 사항은 추상화에 따라 달라집니다.

간단히 말하면 클래스가 다른 특정 클래스(구체적 구현)에 직접 종속되는 대신 인터페이스 또는 추상 클래스에 종속되어야 합니다. 종속 클래스를 변경하지 않고도 구현을 교체할 수 있으므로 코드가 더욱 유연해지고 유지 관리가 쉬워집니다.

밀접하게 결합된 코드(DIP 없음)

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }

  public void saveToFiles() {
    // code to save invoice as a file.
  }
}

위의 예에서 볼 수 있듯이 Computer 클래스는 Keyboard 클래스에 직접적으로 종속됩니다.

느슨하게 결합된 코드(DIP 포함)

public class Invoice {
  private Long InvoiceNo;

  public void generateInvoice() {
    // code to generate Invoice.
  }
}

public class FileManager {
  public void saveToFiles(Invoice invoice) {
    // code to save invoice as a file.
  }
}

이제 컴퓨터는 특정 키보드가 아닌 InputDevice 인터페이스에 의존합니다. 이렇게 하면 Computer 클래스를 수정하지 않고도 WirelessKeyboard와 같은 다른 입력 장치로 쉽게 전환할 수 있습니다.

DIP를 따르면 얻을 수 있는 이점

  • 느슨한 결합: 구체적인 구현이 아닌 추상화에 의존하면 코드 결합이 덜 되므로 다른 부분에 영향을 주지 않고 시스템의 한 부분을 쉽게 변경할 수 있습니다.
  • 유지보수성 향상 : 하위 모듈의 변경 사항이 상위 모듈에 영향을 주지 않으므로 시스템 유지 관리 및 확장이 더 쉬워집니다.
  • 향상된 테스트 가능성 : 하위 수준 모듈의 모의 구현을 사용하여 상위 수준 모듈을 테스트할 수 있으므로 테스트 속도와 안정성이 향상됩니다.
  • 재사용성 증가: 상위 수준 모듈은 의존하는 하위 수준 모듈을 변경할 필요 없이 다양한 상황에서 재사용할 수 있습니다.

결론

결론적으로 SOLID 원칙: 단일 책임, 개방형, 폐쇄형, Liskov 대체, 인터페이스 분리 및 종속성 반전은 객체 지향 프로그래밍에서 깔끔하고 유지 관리 가능하며 확장 가능한 코드를 작성하기 위한 필수 지침을 제공합니다.

이러한 원칙을 준수함으로써 개발자는 더 쉽게 이해하고, 수정하고, 확장할 수 있는 시스템을 만들 수 있으며 궁극적으로 더 높은 품질의 소프트웨어와 더 효율적인 개발 프로세스로 이어질 수 있습니다.

요약

이 글을 읽어주셔서 감사합니다! 이제 SOLID 원칙과 이를 적용하여 프로젝트를 향상시킬 수 있는 방법을 확실하게 이해하셨기를 바랍니다.

팔로우:
  • LinkedIn — @nsadisha
  • GitHub — @nsadisha
  • 중간 — @nsadisha
  • Dev.to — @nsadisha

— 사디샤 님사라

위 내용은 견고한 원칙의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
이전 기사:약간의 Java17다음 기사:약간의 Java17