실제로 열린 원칙을 고수하는 방법


14

공개 원칙의 의도를 이해합니다. 수정하지 않고 확장하도록 지시함으로써 수정하는 동안 이미 작동하는 것을 깨뜨릴 위험을 줄입니다.

그러나이 원칙이 실제로 어떻게 적용되는지 이해하는 데 어려움이있었습니다. 내 이해에는 두 가지 방법이 있습니다. 변경 전과 후에 :

  1. 이전 : 추상화에 프로그래밍하고 가능한 한 '미래를 예측'하십시오. 예를 들어, 향후 시스템에 s가 추가 drive(Car car)되면 방법 이 변경 Motorcycle되어 OCP를 위반할 수 있습니다. 그러나이 방법 drive(MotorVehicle vehicle)은 향후 변경 될 가능성이 적으므로 OCP를 준수합니다.

    그러나 미래를 예측하고 시스템에 어떤 변화가 있을지 미리 알기는 매우 어렵습니다.

  2. 이후 : 변경이 필요한 경우 현재 코드를 수정하는 대신 클래스를 확장하십시오.

연습 # 1은 이해하기 어렵지 않습니다. 그러나 신청 방법을 이해하는 데 어려움이있는 것은 연습 # 2입니다.

예를 들어 (YouTube의 비디오에서 가져 왔습니다) : 클래스에 CreditCard객체 를 허용하는 메소드가 있다고 가정 해 봅시다 makePayment(CraditCard card). 하루 Voucher가 시스템에 추가됩니다. 이 방법은 지원하지 않으므로 수정해야합니다.

처음에 메소드를 구현할 때 미래와 프로그램을 더 추상적 인 용어로 예측하지 못했습니다 (예 : makePayment(Payment pay)이제 기존 코드를 변경해야 함).

실습 # 2는 수정하는 대신 확장하여 기능을 추가해야한다고 말합니다. 그게 무슨 뜻이야? 기존 코드를 단순히 변경하는 대신 기존 클래스를 서브 클래 싱해야합니까? 코드를 다시 쓰지 않기 위해 래퍼를 만들어야합니까?

또는 원칙은 '기능을 올바르게 수정 / 추가하는 방법'을 말하는 것이 아니라 '처음에 프로그램을 변경하지 않아도되는 방법 (예 : 추상화에서 추상화)'을 말하는가?



1
개방 / 폐쇄 원칙은 사용하는 메커니즘을 지시하지 않습니다. 상속은 일반적으로 잘못된 선택입니다. 또한 미래의 모든 변화를 막을 수는 없습니다. 미래를 예측하지 않는 것이 가장 좋지만 일단 변경이 필요한 경우 같은 종류의 향후 변경을 수용 할 수 있도록 디자인을 수정하십시오.
Doval

답변:


14

디자인 원칙은 항상 서로 균형을 이루어야합니다. 미래를 예측할 수 없으며 대부분의 프로그래머는 시도 할 때 끔찍하게 행동합니다. 그렇기 때문에 우리 는 주로 복제에 관한 것이지만 다른 디자인 원칙에 대한 리팩토링에도 적용되는 3 규칙을 사용합니다.

인터페이스를 하나만 구현 한 경우 확장이 수행 될 위치가 명확하지 않으면 OCP에 대해 신경 쓸 필요가 없습니다. 실제로, 이 상황에서 과도하게 설계하려고 할 때 명확성을 잃는 경우가 종종 있습니다. 한 번 확장 하면 가장 쉽고 명확한 방법 인 경우 OCP 친화적으로 만들기 위해 리팩터링 합니다. 세 번째 구현으로 확장 할 때는 조금 더 노력이 필요한 경우에도 OCP를 고려하여 리팩토링해야합니다.

실제로 두 개의 구현 만있는 경우 세 번째를 추가 할 때 리팩토링하는 것은 그리 어렵지 않습니다. 그 시점을지나 성장을 유지하게되면 귀찮게됩니다.


1
대답 해줘서 고마워. 내가 무슨 말을하는지 이해하도록하겠습니다. 당신이하는 말은 주로 수업을 변경 한 후에 주로 OCP에 관심을 가져야한다는 것 입니다. 의미 : 클래스를 처음 구현할 때 미래를 예측하기가 어렵 기 때문에 OCP에 대해 걱정하지 않아도됩니다. 처음으로 확장 / 수정해야 할 경우 나중에 좀 더 융통성이 있도록 조금 리팩터링하는 것이 좋습니다 (더 많은 OCP). 그리고 세 번째로 수업을 확장 / 수정해야하는데, 이제는 OCP를보다 잘 준수하기 위해 리팩토링을해야합니다. 이게 니가하려고했던 거 맞아?
Aviv Cohn

1
그게 아이디어입니다.
Karl Bielefeldt

2

나는 당신이 미래를 너무 멀리보고 있다고 생각합니다. 현재의 문제를 유연하게 개방 / 폐쇄하는 방식으로 해결하십시오.

drive(Car car)메소드 를 구현해야한다고 가정 해 봅시다 . 언어에 따라 몇 가지 옵션이 있습니다.

  • 오버로딩 (C ++)을 지원하는 언어의 경우 drive(const Car& car)

    언젠가는을 (를) 필요 drive(const Motorcycle& motorcycle)로하지만을 (를) 방해하지 않습니다 drive(const Car& car). 문제 없어요!

  • 오버로드를 지원하지 않는 언어 (Objective C)의 경우 메소드에 유형 이름을 포함하십시오 -driveCar:(Car *)car.

    어느 시점에서 당신은 필요할지 -driveMotorcycle:(Motorcycle *)motorcycle모르지만 다시는 방해하지 않을 것입니다.

이를 통해 drive(Car car)수정 이 가능 하지만 다른 차량 유형으로 확장 할 수 있습니다. 이 미니멀리스트 미래 계획을 통해 오늘 작업을 수행 할 수 있지만 미래에 자신을 차단할 수는 없습니다.

필요한 가장 기본적인 유형을 상상하려고하면 무한 회귀로 이어질 수 있습니다. Segue, 자전거 또는 점보 제트기를 운전하고 싶을 때 일어나는 일. 사람들이 이동성을 위해 사용하고 사용하는 모든 장치를 설명 할 수있는 단일 일반 추상 유형을 어떻게 구성합니까?


새 메소드를 추가하기 위해 클래스를 수정하면 공개 폐쇄 원칙을 위반합니다. 귀하의 제안은 또한 운전할 수있는 모든 차량에 Liskov- 대체 원칙을 적용하는 능력을 제거하여 본질적으로 OO의 가장 강력한 부분 중 하나를 제거합니다.
덩크

@ 덩크 나는 엄격한 Meyer 개방 / 폐쇄 원칙이 아닌 다형성 개방 / 폐쇄 원칙에 대한 대답을 기반으로했습니다. 새로운 인터페이스를 지원하도록 클래스를 업데이트 할 수 있습니다. 이 예에서 자동차 인터페이스는 모터 사이클 인터페이스와 별도로 유지됩니다. 이것들은 구현 클래스가 지원할 수있는 자동차와 오토바이에 대한 별도의 운전 추상 클래스로 공식화 될 수 있습니다.
Jeffery Thomas

@ 덩크 Liskov 대체 원칙은 유용하지만 무료로 제공되지는 않습니다. 원래 사양에 자동차 만 있으면 더 일반적인 차량을 만드는 데 추가 비용, 시간 및 복잡성에 대한 가치가 없을 수 있습니다. 또한보다 일반적인 차량이 계획되지 않은 서브 클래스를 처리하는 데 완벽하게 적합하지는 않습니다. 오토바이의 인터페이스는 차량 인터페이스 (자동차 만 취급하도록 설계됨)에 장착해야하며, 오토바이를 처리하도록 차량을 개조해야합니다 (실제로 열림 / 닫힘).
Jeffery Thomas

Liskov-Substitution 원칙은 무료로 제공되지 않지만 많은 비용이 들지 않습니다. 그리고 일반적으로 주 응용 프로그램에서 다른 하위 클래스가 상속되지 않더라도 이전보다 훨씬 더 많은 비용을 갚습니다. LSP를 적용하면 자동화 된 테스트가 훨씬 쉬워집니다. 이미 승리했습니다. 또한, 당신은 확실히 흥분하지 말고 모든 것이 LSP를 필요로한다고 가정하지만, 응용 프로그램을 구축하고 향후 개정판에서 필요할 것 같은 느낌이 들지 않으면 응용 프로그램이나 도메인에 대해 충분히 알고 있어야합니다.
덩크

1
OCP의 정의와 관련하여. 내가 일했던 업종 일 수도 있는데, 일반적인 상용 회사보다 높은 수준의 검증이 필요한 경향이 있지만 일반적으로 파일 / 클래스가 변경되면 파일 / 클래스를 다시 테스트해야 할뿐만 아니라 회귀 테스트에서 해당 파일 / 클래스를 사용합니다. 따라서 누군가가 다형성 개방 / 폐쇄가 양호하다고 말하는 것은 중요하지 않습니다. 인터페이스를 변경하면 광범위한 결과가 발생하므로 그렇게 괜찮지는 않습니다.
덩크

2

공개 원칙의 의도를 이해합니다. 수정하지 않고 확장하도록 지시함으로써 수정하는 동안 이미 작동하는 것을 깨뜨릴 위험을 줄입니다.

또한 기존 객체의 동작을 변경하지 않음으로써 해당 방법에 의존하는 모든 객체를 손상시키지 않습니다. 개체가 동작 변경을 알리면 다른 개체가 해당 동작을 기대하는 것을 정확히 알지 못하고 알려진 동작 및 예상 동작을 변경하므로 위험합니다.

그게 무슨 뜻이야? 기존 코드를 단순히 변경하는 대신 기존 클래스를 서브 클래 싱해야합니까?

예.

"신용 카드 만 허용"은 공용 인터페이스를 통해 해당 클래스의 동작의 일부로 정의됩니다. 프로그래머는이 객체의 메서드는 신용 카드 만 사용한다고 세상에 선언했습니다. 그녀는 특히 명확한 방법 이름을 사용하지 않았지만 그 방법을 사용했습니다. 나머지 시스템은 이것에 의존합니다.

당시에는 의미가 있었지만 지금 변경해야 할 경우 신용 카드 이외의 것을 허용하는 새로운 클래스를 만들어야합니다.

새로운 행동 = 새로운 클래스

제쳐두고 -미래를 예측하는 좋은 방법은 당신이 방법을 지정한 이름에 대해 생각하는 것입니다. 메소드에 특정 규칙이있는 메소드에 makePayment와 같은 일반적인 일반적인 메소드 이름을 지정하여 정확히 어떤 지불을 할 수 있습니까? 그것은 코드 냄새입니다. 특정 규칙이있는 경우 메소드 이름에서이를 명확하게해야합니다. makePayment는 makeCreditCardPayment 여야합니다. 객체를 처음 작성할 때이 작업을 수행하면 다른 프로그래머가이 객체에 대해 감사하게됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.