일부 또는 모든 SOLID 원칙이 코드를 깨끗하게 해석하기 어려운 OOP의 특징이 있습니까?


26

저는 최근 비디오 게임 개발에서 OOP에 대해 제 친구와 토론했습니다.

친구의 놀랍게도 많은 작은 클래스와 여러 추상화 계층이 포함 된 내 게임 중 하나의 아키텍처를 설명하고있었습니다. 나는 이것이 모든 일에 단일 책임을 부여하고 구성 요소 간의 결합을 완화하는 데 중점을 둔 결과라고 주장했다.

그의 우려는 많은 클래스가 유지 보수의 악몽으로 이어질 것이라는 점이었습니다. 내 견해로는 그 반대 효과가있을 것이라는 것이 었습니다. SOLID 원칙과 적절한 OOP가 실제로 잘 혼합되지 않은 사례가있을 수 있다고 말하면서, 우리는 수세기에 걸쳐 이에 대해 논의하면서 결국 동의하지 않을 것에 동의했습니다.

SOLID 원칙에 대한 Wikipedia의 항목 조차도 유지 관리 가능한 코드 작성에 도움 이되는 가이드 라인 이며 민첩하고 적응 형 프로그래밍의 전체 전략의 일부라고 설명합니다.

그래서 내 질문은 :

OOP에 일부 또는 모든 SOLID 원칙이 코드를 정리하지 않는 경우가 있습니까?

Liskov 대체 원칙이 안전한 상속의 다른 풍미와 충돌 할 수 있음을 즉시 상상할 수 있습니다. 즉, 누군가가 상속을 통해 구현 된 다른 유용한 패턴을 고안했다면 LSP와 직접적으로 충돌 할 가능성이 있습니다.

다른 사람이 있습니까? 아마도 특정 유형의 프로젝트 또는 특정 대상 플랫폼이 덜 SOLID 방식으로 더 잘 작동합니까?

편집하다:

코드를 개선하는 방법을 묻지 않도록 지정하고 싶습니다.)이 질문에서 프로젝트를 언급 한 유일한 이유는 약간의 맥락을 제시하는 것이 었습니다. 내 질문은 일반적으로 OOP 및 디자인 원칙에 관한 것 입니다.

내 프로젝트가 궁금하다면을 참조 하십시오 .

편집 2 :

이 질문에 세 가지 방법 중 하나로 대답 할 것이라고 상상했습니다.

  1. 예, SOLID와 부분적으로 충돌하는 OOP 설계 원칙이 있습니다.
  2. 예, SOLID와 완전히 충돌하는 OOP 설계 원칙이 있습니다.
  3. 아니요, SOLID는 꿀벌의 무릎이며 OOP는 영원히 더 좋을 것입니다. 그러나 모든 것과 마찬가지로 만병 통치약이 아닙니다. 책임감있게 마셔 라.

옵션 1과 2는 길고 흥미로운 답변을 생성했을 것입니다. 반면에 옵션 3은 짧고 흥미롭지 않지만 전반적으로 안심할 수있는 대답입니다.

옵션 3에 수렴하고있는 것 같습니다.


'깨끗한 코드'를 어떻게 정의하고 있습니까?
GrandmasterB

이 질문의 범위에서 깨끗한 코드는 OOP의 목표와 선택한 디자인 원칙 세트를 충족시키는 방식으로 코드 구조입니다. 따라서 OOP + SOLID의 목표 설정 측면에서 깨끗한 코드에 익숙합니다. OOP에서 작동하지만 SOLID와 완전히 또는 부분적으로 호환되지 않는 다른 설계 원칙이 있는지 궁금합니다.
MetaFight

1
나는 당신의 그 게임의 성능에 대해 다른 것보다 훨씬 걱정하고 있습니다. 우리가 텍스트 기반 게임에 대해 이야기하지 않는 한.
Euphoric

1
이 게임은 본질적으로 실험입니다. 다른 개발 방법을 시도하고 있으며 게임을 작성할 때 엔터프라이즈 코드가 작동하는지 확인하려고합니다. 성능이 문제가 될 것으로 기대하지만 아직 그렇지 않습니다. 그렇다면 / 다음에 실험 할 것은 코드를 성능 코드에 적용하는 방법입니다. 너무 많은 학습!
MetaFight

1
일부 다운 보트의 위험 에 따라 게임 개발의 OOP 인 "왜"질문으로 돌아가고 싶습니다 . 때문에 게임 개발의 특성, "전통적인"OOP 엔터티 / 구성 요소 시스템, 재미있는 변형의 다양한 맛에 찬성 유행의 떨어지는 것 같다 여기에 예를 들어. 이것은 오히려 질문이 "SOLID + OOP"가 아니라 "OOP + 게임 개발"이어야한다고 생각하게합니다. 게임 개발자를위한 객체 상호 작용 그래프를 볼 때 N-tier 애플리케이션과 비교해 볼 때 차이점은 새로운 패러다임이 좋다는 것을 의미 할 수 있습니다.
J Trana

답변:


20

OOP에 일부 또는 모든 SOLID 원칙이 코드를 정리하지 않는 경우가 있습니까?

일반적으로 아닙니다. 역사는 SOLID 원칙이 모두 디커플링 증가에 크게 기여한다는 것을 보여 주었으며, 결과적으로 코드의 유연성이 향상되어 변경 사항을 수용 할 수있을뿐만 아니라 코드를 쉽게 추론하고 테스트하고 재사용 할 수있게되었습니다. 요컨대, 코드를 더 깨끗하게 만드십시오.

이제 SOLID 원리가 DRY (반복하지 말 것), KISS (간단한 바보로 유지) 또는 다른 우수한 OO 설계 원칙과 충돌하는 경우가 있습니다. 물론 요구 사항의 현실, 인간의 한계, 프로그래밍 언어의 한계, 기타 장애물과 충돌 할 수 있습니다.

요컨대, SOLID 원칙은 항상 코드를 정리하는 데 도움이되지만 일부 시나리오에서는 충돌하는 대안보다 덜 적합합니다. 그들은 항상 좋지만 때로는 다른 것들이 좋습니다.


14
나는 좋아 하지만 때로는 다른 것들이 더 좋습니다 . "동물 농장"느낌이 듭니다. ;)
FrustratedWithFormsDesigner

12
+1 방금 SOLID 원리를 살펴보면 5 개의 "좋아요"가 표시됩니다. 그러나 그들 중 누구도 DRY, KISS를 뛰어 넘지 않으며 "의도를 명확하게한다". SOLID를 사용하되 약간의주의와 회의를 보여주십시오.
user949300

동의한다. 나는 따라야 할 올바른 원칙은 상황에 따라 분명히 달라 지지만 항상 다른 모든 것보다 높은 순위를 요구하는 어려운 성능 요구 사항 (특히 게임 개발에서 중요 함)을 제외하고는 DRY와 KISS가 일반적으로 더 중요하다고 생각합니다. SOLID보다. 물론 코드를 더 깨끗하게 만들수록 충돌없이 모든 원칙을 따를 수 있다면 더 좋습니다.
Ben Lee

통합 분리와 효율적인 집계 설계는 어떻습니까? 기본 "순서"인터페이스 [예를 IEnumerable] 같은 방법이 포함 된 경우 CountasImmutable같은 속성을 getAbilities[그 반환 같은 것들을 여부를 나타냅니다 Count"효율적"입니다] 다음 중 하나가 여러 시퀀스 집계 소요 정적 메서드를 가질 수 그들을 그들은 그렇게 하나의 긴 시퀀스로 작동합니다. 이러한 기능이 기본 시퀀스 유형에 존재하는 경우 기본 구현에만 연결하더라도 집계는 해당 기능을 노출 할 수 있습니다.
supercat

... 그리고 그렇게 할 수있는 기본 클래스와 함께 사용될 때 효율적으로 구현하십시오. 수를 아는 1000 만 개의 항목 목록과 순차적으로 만 항목을 검색 할 수있는 5 개의 항목 목록을 집계하는 경우 10,000,003 번째 항목에 대한 요청은 첫 번째 목록에서 수를 읽고 두 번째 항목에서 3 개의 항목을 읽어야합니다. . 주방 싱크 인터페이스는 ISP를 위반할 수 있지만 일부 구성 / 집계 시나리오의 성능을 크게 향상시킬 수 있습니다.
supercat

13

나는 금융과 게임 모두에서 일했다는 점에서 특이한 관점을 가지고 있다고 생각합니다.

게임에서 만난 많은 프로그래머들은 소프트웨어 엔지니어링에서 끔찍했지만 SOLID와 같은 연습은 필요하지 않았습니다. 전통적으로 그들은 게임을 상자에 넣었습니다.

금융 분야에서 개발자는 게임에서 수행하는 성능이 필요하지 않기 때문에 개발자가 너무 조잡하고 통제가 불가능하다는 것을 알았습니다.

위의 두 진술은 물론 지나치게 일반화되었지만 실제로 두 경우 모두 깨끗한 코드가 중요합니다. 명확하고 이해하기 쉬운 최적화를 위해서는 코드가 필수적입니다. 유지 관리를 위해 동일한 것을 원합니다.

SOLID가 비판이 없다고 말하는 것은 아닙니다. 그것들을 원칙이라고 부르지 만 지침처럼 따라야합니까? 정확히 언제 따라야합니까? 언제 규칙을 어겨 야합니까?

아마도 두 가지 코드를보고 어느 것이 SOLID와 가장 잘 맞는지 말할 수 있습니다. 그러나 코드를 SOLID로 변환하는 객관적인 방법은 없습니다. 개발자의 해석에 달려 있습니다.

"많은 수업이 유지 보수의 악몽을 초래할 수있다"고 말하는 것은 비판적인 일입니다. 그러나 SRP가 올바르게 해석되지 않으면이 문제가 발생하기 쉽습니다.

나는 책임이없는 많은 계층의 코드를 보았습니다. 한 클래스에서 다른 클래스로 상태를 전달하는 것 외에는 아무 것도하지 않는 클래스. 너무 많은 간접 계층의 고전적인 문제.

악용 될 경우 SRP는 코드에 결속력이 부족할 수 있습니다. 기능을 추가하려고 할 때이를 알 수 있습니다. 항상 동시에 여러 장소를 변경해야하는 경우 코드에 응집력이 부족합니다.

공개 폐쇄에는 비평가가 없습니다. 예를 들어 Jon Skeet의 공개 폐쇄 원칙 에 대한 검토를 참조하십시오. . 나는 그의 주장을 여기서 재현하지 않을 것이다.

LSP에 대한 당신의 주장은 다소 가설적인 것처럼 보이며 다른 형태의 안전한 상속의 의미를 이해하지 못합니까?

ISP가 SRP를 다소 복제 한 것 같습니다. 인터페이스의 모든 클라이언트가 무엇을하는지 예상 할 수없는 것과 동일하게 해석해야합니다. 오늘날의 응집력있는 인터페이스는 미래의 ISP 위반자입니다. 비논리적 인 결론은 인터페이스 당 멤버를 두 명 이하로 두는 것입니다.

DIP는 사용할 수없는 코드로 이어질 수 있습니다. DIP라는 이름으로 많은 수의 매개 변수가있는 클래스를 보았습니다. 이 클래스는 일반적으로 복합 부분을 "새롭게"만들었지 만 테스트 가능성이라는 이름으로 새 키워드를 다시 사용해서는 안됩니다.

SOLID 자체만으로는 깨끗한 코드를 작성하기에 충분하지 않습니다. Robert C. Martin의 저서 " 깨끗한 코드 : 민첩한 소프트웨어 기술의 핸드북 "을 보았습니다 . 이 책의 가장 큰 문제점은이 책을 읽고 규칙으로 해석하기 쉽다는 것입니다. 그렇게하면 요점이 없어집니다. 여기에는 SOLID의 5 개 이상의 냄새, 휴리스틱 및 원리 목록이 포함되어 있습니다. 이러한 '원칙'은 모두 원칙이 아니라 지침입니다. 밥 아저씨는 그것들을 개념에 대한 "취약한 구멍"으로 자신을 묘사합니다. 그들은 균형을 잡는 행위입니다. 맹목적으로 지침을 따르면 문제가 발생할 수 있습니다.


1
생성자 매개 변수가 많으면 SRP를 위반 한 것입니다. 그러나 DI 컨테이너는 어쨌든 많은 생성자 매개 변수와 관련된 고통을 제거하므로 DIP에 대한 귀하의 진술에 동의하지 않습니다.
Stephen

이러한 '원칙'은 모두 원칙이 아니라 지침입니다. 그들은 균형을 잡는 행위입니다. SRP를 따르는 게시물에서 말했듯이 극단적 인 자체는 문제가 될 수 있습니다.
Dave Hillier

좋은 대답, +1 한 가지 더하고 싶은 것은 Skeet의 리뷰에서 OCP의 핵심 아이디어가 아니라 OCP에 대한 표준 교과서 정의에 대한 비평가를보다 많이 읽었습니다. 내 이해에 따르면, 보호 변형 아이디어는 OCP가 처음부터 있었던 것입니다.
Doc Brown

5

내 의견은 다음과 같습니다.

SOLID 원칙은 중복되지 않고 유연한 코드베이스를 목표로하지만 클래스와 레이어가 너무 많으면 가독성과 유지 관리 측면에서 절충이 될 수 있습니다.

특히 추상화 수에 관한 것입니다 . 소수의 추상화에 기반을 둔 경우 많은 클래스가 정상일 수 있습니다. 우리는 프로젝트의 크기와 특성을 알지 못하기 때문에 말하기가 어렵지만 많은 클래스 외에도 여러 계층 을 언급하는 것은 약간 걱정입니다 .

SOLID 원칙이 OOP에 동의하지 않는 것 같지만 그 규칙을 준수하지 않는 코드를 작성할 수는 있습니다.


3
+1 정의에 따르면, 유연성이 높은 시스템은 유연성이 떨어지는 시스템보다 유지 관리가 용이하지 않아야합니다. 유연성은 추가 된 복잡성으로 인해 비용이 발생합니다.
Andy

@ 앤디는 아닙니다. 현재 직장에서 코드의 최악의 부분은 "단순한 일을 해킹하고 함께 해킹하고 그렇게 복잡하게 만들지 않음"에 의해 함께 던져지기 때문에 지저분합니다. 결과적으로 시스템의 많은 부분이 100 % 단단합니다. 큰 부분을 다시 쓰지 않으면 수정할 수 없습니다. 유지하기가 정말 어렵습니다. 유지 보수성은 시스템의 유연성이 아니라 높은 응집력과 낮은 결합력에서 비롯됩니다.
사라

3

개발 초기에 C ++로 로그 라이크를 작성하기 시작했습니다. 교육에서 배운 훌륭한 객체 지향 방법론을 적용하고 싶을 때 즉시 게임 항목을 C ++ 객체로 보았습니다. Potion, Swords, Food, Axe, Javelins등 모든 기본 코어에서 파생 된 Item이름, 아이콘, 무게, 그런 종류의 물건을 처리하는 코드.

그런 다음 백 로직을 코딩하고 문제에 직면했습니다. Items가방에 넣을 수는 있지만, 하나를 선택하면 a Potion인지 a 인지 어떻게 알 수 Sword있습니까? 인터넷에서 항목을 다운 캐스트하는 방법을 찾았습니다. 런타임 정보를 활성화하기 위해 컴파일러 옵션을 해킹하여 추상화 된 Item실제 유형이 무엇인지 알 수 있고 유형에 대한 논리 전환을 시작하여 허용 할 작업을 알았습니다 (예 : 음주를 피하고 발에 Swords착용 Potions)

그것은 본질적으로 반 복사 붙여진 진흙의 큰 공으로 코드를 돌 렸습니다. 프로젝트가 진행되면서 디자인 실패가 무엇인지 이해하기 시작했습니다. 일어난 일은 클래스를 사용하여 값을 나타내려고했습니다. 수업 계층 구조는 의미있는 추상화가 아니 었습니다. 내가 뭘 했어야하는 것은 만드는 Item등의 기능 세트를 구현 Equip (), Apply ()그리고 Throw (), 그리고 값을 기준으로 각없이 행동을합니다. 열거 형을 사용하여 장착 슬롯을 나타냅니다. 열거 형을 사용하여 다른 무기 종류를 나타냅니다. 하위 클래스에는 이러한 최종 값을 채우는 것 외에 다른 목적이 없었기 때문에 더 많은 값을 사용하고 분류는 줄였습니다.

추상화 계층에서 객체 곱셈에 직면하고 있기 때문에 여기에서 내 통찰력이 가치 있다고 생각합니다. 객체 유형이 너무 많은 것 같으면 유형이어야하는 것과 값이어야하는 것을 혼동하는 것일 수 있습니다.


2
문제는 유형을 값 클래스와 혼동하는 것이 었습니다 (참고 : 클래스의 OOP / C ++ 개념을 참조하기 위해 class라는 단어를 사용하고 있지 않습니다.) 직교 평면의 점을 나타내는 여러 가지 방법이 있습니다 (직사각 좌표, 극좌표 좌표) 그러나 그들은 모두 같은 유형의 일부입니다. 그러나 주류 OOP 언어는 자연스럽게 값 분류를 지원하지 않습니다. 그것에 가장 가까운 것은 C / C ++의 조합이지만, 거기에 넣은 것이 무엇인지 알기 위해 여분의 필드를 추가해야합니다.
Doval

4
당신의 경험은 반 예제로 사용될 수 있습니다. SOLID 또는 모든 종류의 모델링 방법을 사용하지 않으면 유지 관리 할 수없고 확장 할 수없는 코드가 작성되었습니다.
Euphoric

1
@Euphoric 나는 열심히 노력했다. 객체 지향 방법론이있었습니다. 그것을 구현 성공적으로 방법론을 가진 사이의 차이는 :하지만,이
아서 블리 첵

2
이것이 OOP에서 코드를 정리하는 것과 반대로 실행되는 SOLID 원칙의 예는 무엇입니까? 잘못된 디자인의 예인 것 같습니다. 이것은 OOP와 직교입니다!
Andres F.

1
기본 ITEM 클래스에는 기본적으로 FALSE로 설정되는 여러 "canXXXX"부울 메소드가 있어야합니다. 그런 다음 Javelin 클래스에서 "canThrow ()"를 true로 설정하고 Postion 클래스에서 canEat ()를 true로 반환 할 수 있습니다.
James Anderson

3

그의 우려는 많은 클래스가 유지 보수의 악몽으로 이어질 것이라는 점이었습니다. 내 견해로는 그 반대 효과가있을 것이라는 것이 었습니다.

나는 절대적으로 친구의 편이지만, 그것은 우리의 영역과 우리가 다루는 문제와 디자인의 유형, 특히 미래에 어떤 유형의 것들이 변화를 요구할 수 있는지의 문제 일 수 있습니다. 다른 문제, 다른 해결책. 나는 옳고 그름을 믿지 않고 단지 특정 설계 문제를 가장 잘 해결할 수있는 최선의 방법을 찾으려고 노력하는 프로그래머입니다. 나는 게임 엔진과 다르지 않은 VFX에서 일합니다.

그러나 적어도 SOLID를 준수하는 아키텍처 (COM 기반 이었음)라고 부르는 문제에서 어려움을 겪고있는 문제는 다음과 같이 "너무 많은 클래스"또는 "너무 많은 함수"로 요약 될 수 있습니다. 친구가 설명 할 수 있습니다. "너무 많은 상호 작용, 오작동을 일으킬 수있는 많은 장소, 부작용을 일으킬 수있는 너무 많은 장소, 변화가 필요할 수있는 너무 많은 장소, 우리가 생각하는 것을하지 않을 너무 많은 장소 "

우리는 하위 유형의 보트로드에 의해 구현 된 소수의 추상적이고 순수한 인터페이스를 가졌습니다 (ECS 이점에 대해 이야기하면서 왼쪽 아래 주석을 무시 하면서이 다이어그램을 만들었습니다).

여기에 이미지 설명을 입력하십시오

모션 인터페이스 또는 장면 노드 인터페이스가 조명, 카메라, 메시, 물리 솔버, 쉐이더, 텍스처, 뼈, 기본 모양, 곡선 등 수백 가지 하위 유형으로 구현 될 수있는 경우 (각각 여러 유형이 있음) ). 그리고 궁극적 인 문제는 실제로 그 디자인이 안정적이지 않다는 것입니다. 우리는 변화하는 요구 사항이 있었고 때로는 인터페이스 자체도 변경해야했으며 200 개의 하위 유형으로 구현 된 추상 인터페이스를 변경하려는 경우 비용이 많이 듭니다. 우리는 그 사이에 추상적 인 기본 클래스를 사용함으로써 그러한 설계 변경 비용을 줄 였지만 여전히 비싸다는 것을 완화하기 시작했습니다.

대안으로 게임 산업에서 일반적으로 사용되는 엔터티 구성 요소 시스템 아키텍처를 살펴보기 시작했습니다. 모든 것이 다음과 같이 바뀌 었습니다.

여기에 이미지 설명을 입력하십시오

와우! 그것은 유지 보수성 측면에서 그러한 차이였습니다. 종속성은 더 이상 추상화 를 향한 것이 아니라 데이터 (구성 요소)를 향했습니다 . 그리고 필자의 경우 최소한 변화하는 요구 사항에도 불구하고 설계 측면에서 데이터를 훨씬 더 안정적이고 쉽게 얻을 수있었습니다.

또한 ECS의 엔터티는 상속 대신 컴포지션을 사용하므로 실제로 기능을 포함 할 필요는 없습니다. 그것들은 단지 유사한 "구성 요소의 컨테이너"입니다. 따라서 모션 인터페이스 를 구현 한 200 개의 하위 유형이 모션 구성 요소 (모션과 관련된 데이터 일뿐)를 저장하는 200 개의 엔티티 인스턴스 (별도의 코드가있는 별도 유형이 아님) 로 바뀝니다 . A 는 더 이상 별도의 클래스 / 하위 유형이 아닙니다. 전혀 수업이 아닙니다. 공간의 위치 (움직임)와 점 조명의 특정 속성과 관련된 일부 구성 요소 (데이터)를 결합한 개체의 인스턴스입니다. 이와 관련된 유일한 기능은 시스템 내부에 있습니다.PointLightRenderSystem장면 렌더링 방법을 결정하기 위해 장면에서 조명 구성 요소를 찾습니다.

ECS 접근 방식에 따라 요구 사항이 변경됨에 따라 해당 데이터에서 작동하는 하나 또는 두 개의 시스템을 변경하거나 측면에 새로운 시스템을 도입하거나 새로운 데이터가 필요한 경우 새로운 구성 요소를 도입해야하는 경우가 종종있었습니다.

따라서 적어도 내 도메인의 경우 모든 사람에게 해당되는 것은 아니라고 확신합니다. 종속성이 안정성 (자주 변경하지 않아도되는 것)으로 흘러 가기 때문에 훨씬 쉽게 만들었습니다. COM 아키텍처에서는 종속성이 추상화를 향해 균일하게 흐를 때 그렇지 않았습니다. 필자의 경우 가능한 모든 작업보다는 모션에 필요한 데이터를 파악하는 것이 훨씬 쉽습니다. 새로운 요구 사항이 들어 오면 몇 개월 또는 몇 년에 걸쳐 약간 변경되는 경우가 많습니다.

여기에 이미지 설명을 입력하십시오

OOP에 일부 또는 모든 SOLID 원칙이 코드를 정리하지 않는 경우가 있습니까?

글쎄, 깨끗한 코드 어떤 사람들은 깨끗한 코드를 SOLID와 동일시하기 때문에 말할 수 없지만 ECS와 마찬가지로 기능에서 데이터를 분리하고 추상화에서 데이터로 종속성을 리디렉션하여 일을 훨씬 쉽게 할 수있는 경우가 있습니다. 데이터가 추상화보다 훨씬 안정적 일 경우 명백한 결합 이유 때문에 변경됩니다. 물론 데이터에 대한 의존성은 불변을 유지하는 것을 어렵게 만들 수 있지만 ECS는 시스템 구성 요소를 사용하여 주어진 유형의 구성 요소에 액세스하는 시스템의 수를 최소화하여이를 최소화하는 경향이 있습니다.

DIP가 제안한 것처럼 종속성이 추상화로 흘러가는 것은 아닙니다. 의존성은 미래의 변화가 필요할 것 같지 않은 것들로 흘러 가야합니다. 그것은 모든 경우에 추상화 일 수도 있고 아닐 수도 있습니다 (확실히 내 것이 아니 었습니다).

  1. 예, SOLID와 부분적으로 충돌하는 OOP 설계 원칙이 있습니다.
  2. 예, SOLID와 완전히 충돌하는 OOP 설계 원칙이 있습니다.

ECS가 실제로 OOP의 맛인지 확실하지 않습니다. 어떤 사람들은 그것을 그런 식으로 정의하지만, 결합 특성과 기능 (시스템)과 데이터 (구성 요소)의 분리 및 데이터 캡슐화 부족과 본질적으로 다른 것으로 보입니다. 그것이 OOP의 형태로 간주된다면, 나는 그것이 SOLID (적어도 SRP, 개방 / 폐쇄, liskov 대체 및 DIP의 가장 엄격한 아이디어)와 충돌하는 것으로 생각합니다. 그러나 나는 이것이 사람들이 일반적으로 더 잘 이해할 수있는 OOP 맥락에서 해석 할 수있는 것처럼 SOLID의 가장 근본적인 측면이 적용되지 않을 수있는 사례와 도메인의 합리적인 예라고 생각합니다.

조그마한 수업

친구의 놀랍게도 많은 작은 클래스와 여러 추상화 계층이 포함 된 내 게임 중 하나의 아키텍처를 설명하고있었습니다. 나는 이것이 모든 일에 단일 책임을 부여하고 구성 요소 간의 결합을 완화하는 데 중점을 둔 결과라고 주장했다.

ECS는 나의 견해에 많은 도전과 변화를 주었다. 당신과 마찬가지로, 유지 보수성이라는 아이디어는 가능한 것들에 대한 가장 간단한 구현을 가지고 있다고 생각했습니다. 하나의 클래스 또는 함수를 확대하여 가장 간단하고 간단한 구현을보고 싶을 때 가장 합리적이며, 보이지 않으면 리팩터링하고 더 분해 할 수도 있습니다. 그러나 상대적으로 복잡한 것을 둘 이상으로 나눌 때마다 두 개 이상의 것이 필연적으로 상호 작용해야합니다 (아래 참조). 방법, 또는 외부의 무언가가 그들 모두와 상호 작용해야합니다.

요즘 나는 무언가의 단순성과 얼마나 많은 것들과 필요한 상호 작용 사이에 균형 잡힌 행동이 있다는 것을 알았습니다. ECS의 시스템은 PhysicsSystemor RenderSystem또는 or 와 같은 데이터에서 작동하는 사소한 구현으로 다소 무겁습니다 GuiLayoutSystem. 그러나 복잡한 제품에 필요한 제품이 너무 적기 때문에 전체 코드베이스의 전반적인 동작을 쉽게 뒤로 물러서서 추론 할 수 있습니다. 거기에는 무언가 더 적고 더 큰 클래스 (여전히 논란의 여지가있는 단일 책임을 수행 함)의 측면에 기대는 것이 좋지 않을 수도 있음을 암시 할 수 있습니다. 시스템.

상호 작용

추상화를 사용하여 두 개의 구체적인 객체를 분리 할 수는 있지만 여전히 서로 대화하기 때문에 "커플 링"이 아닌 "상호 작용"(인터랙션 감소는 둘 다 감소를 의미 함)이라고 말합니다. 그들은이 간접적 인 의사 소통 과정에서 여전히 부작용을 일으킬 수 있습니다. 그리고 종종 시스템의 정확성에 대해 추론 할 수있는 능력이 "커플 링"보다 이러한 "상호 작용"과 관련이 있다는 것을 알게되었습니다. 상호 작용을 최소화하면 조감도에서 모든 것을 추론하기가 훨씬 쉬워집니다. 즉, ECS는 전혀 말을하지 않는 것을 의미합니다. 그런 의미에서 ECS는 또한 커플 링이 아닌 "상호 작용"을 최소한의 최소값까지 최소화하는 경향이 있습니다 (적어도 나는

즉, 이것은 적어도 부분적으로 나와 내 개인적인 약점 일 수 있습니다. 나는 거대한 규모의 시스템을 만들고 자신에 대해 자신있게 추론하고, 탐색하고, 예측 가능한 방식으로 어디서나 잠재적으로 원하는 변경을 할 수 있다고 느끼는 가장 큰 장애를 발견했습니다. 부작용. 내가 직접 작성한 코드조차도 수만 개의 LOC에서 수십만 개의 LOC로 수백만 개의 LOC로 갈 때 발생하는 가장 큰 장애물입니다. 무언가가 무엇보다 크롤링 속도를 늦추는 경우 응용 프로그램 상태, 데이터, 부작용 측면에서 무슨 일이 일어나고 있는지 더 이상 이해할 수 없다는 의미입니다. 그것' 시스템이 내 생각에 대한 추론 능력을 넘어서게되면 변화의 전체 영향을 이해할 수 없을 정도로 속도를 늦추는 데 필요한 로봇 시간이 아닙니다. 그리고 상호 작용을 줄이는 것은 상호 작용을 최소한으로 줄이면 가능한 많은 수의 장소를 줄이기 때문에 개인적으로 이러한 것들에 압도 당하지 않고 훨씬 더 많은 기능으로 제품을 더 크게 성장시킬 수있는 가장 효과적인 방법이었습니다. 심지어 응용 프로그램 상태를 변경하고 실질적으로 부작용을 일으킬 수도 있습니다.

이것은 다이어그램의 모든 것이 기능을 가지고 있으며 실제 시나리오는 많은 수의 객체를 가질 것입니다. 이것은 커플 링으로서가 아니라 "상호 작용"다이어그램입니다. 하나 사이에 추상화가 있습니다)

여기에 이미지 설명을 입력하십시오

... 시스템에만 기능이있는 여기 (파란색 구성 요소는 이제 데이터 일뿐이며 커플 링 다이어그램입니다) :

여기에 이미지 설명을 입력하십시오

SOLID와 더 호환되는 OOP 컨텍스트에서 이러한 이점 중 일부를 구체화하는 방법과 어쩌면 이러한 이점 중 일부를 구상하는 방법이 있지만 아직 디자인과 단어를 아직 찾지 못했습니다. 용어가 OOP와 직접 관련된 모든 것을 던지곤했기 때문에 어렵습니다. 나는 여기에 사람들의 대답을 읽고 그것을 이해하고 나 자신을 공식화하기 위해 최선을 다하려고 노력하고 있지만 ECS의 본질에 대해 매우 흥미로운 점이 있습니다. 사용하지 않는 아키텍처에도 더 광범위하게 적용 할 수 있습니다. 또한이 답변이 ECS 프로모션으로 나오지 않기를 바랍니다. ECS를 설계 한 결과 제 생각이 크게 바뀌었기 때문에 매우 흥미 롭습니다.


나는 상호 작용과 커플 링의 차이를 좋아합니다. 첫 번째 상호 작용 다이어그램은 난독 화되어 있지만 평면 그래프이므로 화살표를 건너 갈 필요가 없습니다.
D Drmmr

2

SOLID 원칙은 좋지만 충돌시 KISS와 YAGNI가 우선합니다. SOLID의 목적은 복잡성을 관리하는 것이지만 SOLID 자체를 적용하면 코드가 더 복잡해지면 목적을 무효화합니다. 예를 들어, 클래스가 거의없는 작은 프로그램이있는 경우 DI, 인터페이스 분리 등을 적용하면 프로그램의 전체적인 복잡성이 크게 증가 할 수 있습니다.

개방 / 폐쇄 원칙은 특히 논쟁의 여지가 있습니다. 기본적으로 서브 클래스를 만들거나 동일한 인터페이스를 별도로 구현하여 기능을 클래스에 추가해야하지만 원래 클래스는 그대로 유지해야합니다. 조정되지 않은 클라이언트가 사용하는 라이브러리 또는 서비스를 유지 관리하는 경우 종료 클라이언트가 의존 할 수있는 동작을 중단하지 않기 때문에 이치에 맞습니다. 그러나 자신의 응용 프로그램에서만 사용되는 클래스를 수정하는 경우 단순히 클래스를 수정하는 것이 훨씬 간단하고 깔끔합니다.


OCP에 대한 논의에서는 컴포지션을 통해 클래스의 동작을 확장 할 가능성을 무시합니다 (예 : 클래스에서 사용하는 알고리즘을 변경할 수 있도록 전략 오브젝트 사용). 일반적으로 서브 클래 싱보다 원칙을 준수하는 것이 더 좋습니다. .
Jules

1
KISS와 YAGNI가 항상 우선권을 갖는다면 여전히 1과 0으로 코딩해야합니다. 어셈블러는 잘못 될 수있는 다른 것입니다. KISS와 YAGNI는 많은 디자인 작업 비용 중 두 가지를 지적하지 않습니다. 이 비용은 설계 작업의 이점과 항상 균형을 이루어야합니다. SOLID는 경우에 따라 비용을 보충하는 이점이 있습니다. 언제 라인을 넘어 갔는지 알 수있는 간단한 방법은 없습니다.
candied_orange

@ CandiedOrange : 기계어 코드가 고급 언어 코드보다 간단하다는 데 동의하지 않습니다. 기계 코드는 추상화 부족으로 인해 우연히 많은 복잡성을 포함하므로 기계 코드로 일반적인 응용 프로그램을 작성하기로 결정한 것은 아닙니다.
JacquesB

@Jules : 그렇습니다. 작곡이 바람직하지만, 작곡을 사용자 정의하는 데 사용할 수있는 방식으로 원래 클래스를 디자인해야하므로 향후 어떤 측면을 사용자 정의해야하는지, 어떤 측면을 가정해야하는지 가정해야합니다. 사용자 정의 할 수 없습니다. 경우에 따라이 작업을 수행해야하지만 (예 : 배포 용 라이브러리 작성) 비용이 들기 때문에 SOLID를 따르는 것이 항상 최적 인 것은 아닙니다 .
JacquesB

1
@JacquesB-맞습니다. 그리고 OCP의 핵심은 YAGNI입니다. 그러나이를 달성하려면 나중에 개선 할 수 있도록 확장 점을 설계해야합니다. 두 이상 사이에서 적절한 균형점을 찾는 것은 까다 롭습니다.
Jules

1

내 경험으로는 :-

큰 클래스는 커질수록 유지하기가 더 어려워집니다.

작은 클래스는 유지 관리가 쉽고 작은 클래스 모음을 유지 관리하는 데 어려움이 클래스 수에 따라 산술적으로 증가합니다.

때때로 큰 수업은 피할 수 없으며 큰 문제는 때때로 큰 수업이 필요하지만, 읽고 이해하기가 훨씬 더 어렵습니다. 이름이 작은 클래스의 경우 이름만으로도 이해하기에 충분할 수 있습니다. 클래스에 특별한 문제가없는 한 코드를 볼 필요조차 없습니다.


0

나는 항상 솔리드의 'S'와 (구식 일 수도 있음) 객체 사이에 선을 그리는 것이 어렵다는 것을 알게된다.

15 년 전 저는 성배를 가진 Deplhi 개발자들과 협력하여 모든 것을 포함한 수업을 진행했습니다. 따라서 모기지의 경우 보험 및 지불, 소득 및 대출 및 세금 정보를 추가 할 수 있으며 납세, 세금 공제를 계산할 수 있으며 직렬화, 디스크 등을 유지할 수 있습니다.

그리고 이제는 훨씬 더 작은 단위로 테스트 할 수 있지만 전체 비즈니스 모델에 대한 좋은 개요를 제공하지 않는다는 이점이있는 많은 작은 클래스에서 동일한 객체가 고안되었습니다.

그리고 네, 객체를 데이터베이스에 묶고 싶지는 않지만 큰 모기지 클래스에 리포지토리를 주입하여 해결할 수 있습니다.

게다가 아주 작은 일을하는 클래스의 좋은 이름을 찾기가 항상 쉬운 것은 아닙니다.

따라서 'S'가 항상 좋은 아이디어인지 확실하지 않습니다.

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