SOLID vs. 조기 추상화 방지


27

나는 모듈성이 중요하고 목표가 명확하게 유용한 상황에서 SOLID 가 무엇 을 달성하고 정기적으로 사용 하는지 이해 합니다. 그러나 두 가지로 인해 코드베이스에서 일관되게 적용 할 수 없습니다.

  • 조기 추상화를 피하고 싶습니다. 내 경험상 구체적인 유스 케이스 (현재 또는 가까운 미래 에 존재하지 않는)없이 추상화 라인을 그리는 것은 잘못된 위치에 그려집니다. 이러한 코드를 수정하려고하면 추상화 줄이 도움이되지 않습니다. 따라서 추상화 선이 어디에 유용 할 지 알 때까지 추상화 선을 그리지 않는쪽에 실수를 저지르는 경향이 있습니다.

  • 내 코드를 더 장황하게 이해하고 이해하기 어렵게 만들고 중복을 제거하지 않으면 자체 모듈에 대한 증가하는 모듈성을 정당화하기가 어렵습니다. 흐름이 단순하고 선형이기 때문에 간단하고 밀접하게 결합 된 절차 또는 신 객체 코드가 매우 잘 구성된 라비올리 코드보다 이해하기 쉽습니다. 쓰기도 훨씬 쉽습니다.

다른 한편으로,이 사고 방식은 종종 신의 대상으로 인도합니다. 나는 일반적으로 이것들을 보수적으로 리팩토링하고, 명확한 패턴이 나타날 때에 만 명확한 추상화 라인을 추가합니다. 모듈성이 더 필요하지 않고 중복이없고 코드를 읽을 수 있다면 God 객체와 밀접하게 결합 된 코드에 어떤 문제가 있는가?

편집 : 개별 SOLID 원칙에 이르기까지 Liskov 대체는 상식의 형식화 IMHO이며 모든 곳에 적용해야 함을 강조하고 싶습니다. 또한 모든 클래스는 추상화 수준에서 하나의 책임을 져야하지만, 구현 세부 사항이 모두 하나의 거대한 2,000 개의 라인 클래스로 채워져 있으면 매우 높은 수준 일 수 있습니다. 기본적으로 추상화는 추상화하기로 선택한 곳에서 의미가 있어야합니다. 모듈화가 명확하게 유용하지 않은 경우 질문하는 원리는 개방형, 인터페이스 분리 및 특히 의존성 반전입니다. 추상화는 의미가 아니라 모듈성에 관한 것이기 때문입니다.


10
@ S.Lott : "more"라는 키워드가 있습니다. 이것은 정확히 YAGNI가 발명 한 것입니다. 단순히 모듈화를 늘리거나 커플 링을 줄이려면 단순히 카고 컬트 프로그래밍입니다.
메이슨 휠러

3
@ S.Lott : 문맥에서 분명해야합니다. 내가 읽으면서 적어도 "문제의 코드에 현재 존재하는 것보다 더 많은 것이 있습니다."
메이슨 휠러

3
@ S.Lott : 만약 당신이 pedantic을 주장한다면, "more"는 현재 존재하는 것 이상을 의미합니다. 코드 또는 거친 디자인이든 무언가가 이미 존재하며 리팩토링하는 방법에 대해 생각하고 있다는 의미입니다.
dsimcha

5
@ back2dos : 그렇습니다. 그러나 확장 가능성에 대한 명확한 아이디어없이 확장 성을 위해 무언가를 설계하는 것에 회의적입니다. 이 정보가 없으면 추상화 라인이 잘못된 위치에있을 수 있습니다. 추상화 라인이 어디에 유용할지 모른다면, 거기에 신 객체가 몇 개 있더라도 작동하고 읽을 수있는 가장 간결한 코드를 작성해야한다고 제안합니다.
dsimcha

2
@ dsimcha : 코드를 작성한 후에 요구 사항이 변경되면 운이 좋습니다. 일반적으로 수행하는 동안 요구 사항이 변경 되기 때문 입니다. 이것이 스냅 샷 구현이 실제 소프트웨어 수명주기를 견뎌내기에 적합하지 않은 이유입니다. 또한 SOLID는 맹목적으로 추상화 할 것을 요구하지 않습니다. 추상화의 컨텍스트는 종속성 반전을 통해 정의됩니다. 모든 모듈의 의존성을 다른 모듈에 의존하는 가장 단순하고 명확한 추상화로 줄입니다. 그것은 임의적이거나 인공적이거나 복잡하지 않지만 잘 정의되고 그럴듯하며 단순합니다.
back2dos

답변:


8

다음은 시스템 설계의 균형을 맞추는 방법을 이해하는 데 적용 할 수있는 간단한 원칙입니다.

  • 단일 책임 원칙 : SSOLID. 메소드 수 또는 데이터 양 측면에서 매우 큰 오브젝트를 가질 수 있으며 여전히이 원칙을 유지합니다. Ruby String 객체를 예로 들어 보겠습니다. 이 객체에는 스틱을 흔드는 것보다 더 많은 방법이 있지만 여전히 응용 프로그램에 대한 일련의 텍스트를 보유하는 것은 하나의 책임입니다. 물체가 새로운 책임을지기 시작하자마자 그것에 대해 열심히 생각하십시오. 유지 관리 문제는 "나중에 문제가있을 경우이 코드를 어디에서 찾을 수 있습니까?"
  • 단순성 계산 : 알버트 아인슈타인은 "모든 것을 가능한 한 단순하게 만들지 말고 단순하게 만들라"고 말했다. 그 새로운 방법이 정말로 필요합니까? 기존 기능으로 필요한 것을 달성 할 수 있습니까? 실제로 새로운 방법이 필요하다고 생각되면 필요한 방법을 모두 충족시키기 위해 기존 방법을 변경할 수 있습니까? 본질적으로 새로운 것을 만들 때 리팩터링합니다.

본질적으로 소프트웨어를 유지 보수 할 때가되면 발에 쏠 수없는 일을하려고합니다. 큰 객체가 합리적인 추상화라면 클래스를 X 라인 / 메소드 / 속성 / 등과 같지 않아야한다는 메트릭을 누군가가 생각해 냈기 때문에이를 분리 할 이유가 거의 없습니다. 어쨌든, 이는 지침 이며 엄격하고 빠른 규칙은 아닙니다.


3
좋은 의견. 다른 답변에서 논의 된 바와 같이, "단일 책임"의 한 가지 문제점은 클래스의 책임이 다양한 추상화 수준에서 설명 될 수 있다는 것입니다.
dsimcha

5

나는 당신이 자신의 질문에 대부분 대답했다고 생각합니다. SOLID는 개념을 추상화 수준으로 높여야 할 때 코드를 리팩토링하는 방법에 대한 지침입니다.

다른 모든 창조적 인 학문과 마찬가지로 절대적 요소는 없으며 트레이드 오프 만 있습니다. 더 많은 일을할수록 현재 문제 도메인이나 요구 사항에 충분한시기를 결정하게됩니다.

추상화는 소프트웨어 개발의 핵심입니다. 따라서 그렇게하지 않는 좋은 이유가 없다면, 연습을 통해 더 나은 결과를 얻을 수 있으며, 트레이드 오프에 대한 느낌을 갖게 될 것입니다. 해를 끼치 지 않는다면 선호하십시오.


5
I want to avoid premature abstraction.In my experience drawing abstraction lines without concrete use cases... 

이것은 부분적으로 옳고 부분적으로 잘못되었습니다.

잘못
이것은 OO 원칙 / SOLID를 적용하지 못하게하는 이유가 아닙니다. 너무 일찍 적용하는 것만 막습니다 .

어느 ...

올바른
코드를 리팩터링 할 때까지 리팩터링하지 마십시오. 요구 사항이 완료되면. 또는 "사용 사례"가 모두 말한대로있을 때.

I find simple, tightly coupled procedural or God object code is sometimes easier to understand than very well-factored...

혼자 또는 사일로에서 작업 할 때
OO를 수행하지 않는 문제는 즉시 명백하지 않습니다. 프로그램의 첫 번째 버전을 작성한 후 :

Code is fresh in your head
Code is familiar
Code is easy to mentally compartmentalize
You wrote it

이 좋은 것의 3/4는 짧은 시간에 빨리 죽습니다.

마지막으로, 당신은 당신이 신의 대상 (많은 기능을 가진 대상)을 가지고 있다는 것을 인식하고 개별적인 기능을 인식합니다. 캡슐화. 폴더에 넣으십시오. 가끔 인터페이스를 사용하십시오. 장기적인 유지 보수를 선호하십시오. 특히 리팩토링되지 않은 방법은 무한히 팽창하여 하나님의 방법이되기 때문입니다.


에서 OO를하지 않는 문제는 즉시 눈에 able니다. 좋은 OO 코드는 적어도 다소 자체 문서화되고 읽을 수 있습니다. 특히 좋은 녹색 코드와 결합 된 경우. 또한 구조가 부족하면 작업과 통합을 훨씬 더 복잡하게 분리하기가 어렵습니다.


권리. 나는 물체가 두 가지 이상의 일을한다는 것을 모호하게 인식하지만 유지 보수가 필요할 때 추상화 라인이 올바른 위치에 놓 이도록 유용하게 분리 하는 방법을 더 구체적으로 인식하지 못합니다 . 유지 보수 프로그래머가 어쨌든 올바른 위치에 추상화 라인을 가져 오기 위해 더 많은 리팩토링을 수행해야 할 경우 리팩토링하는 데 시간이 낭비되는 것처럼 보입니다.
dsimcha

캡슐화와 추상화는 서로 다른 개념입니다. 캡슐화는 기본적으로 클래스가 있음을 의미하는 기본 OO 개념입니다. 클래스는 그 자체로 모듈 성과 가독성을 제공합니다. 유용합니다.
P.Brian.Mackey

추상화는 제대로 적용하면 유지 관리를 줄일 수 있습니다. 잘못 적용하면 유지 보수가 늘어날 수 있습니다. 언제 그리고 어디에서 라인을 그릴 지 알기 위해서는 "공장 패턴을 적용하는"방법 자체의 기본적인 기술적 측면뿐만 아니라 비즈니스, 프레임 워크, 고객에 대한 확실한 이해가 필요합니다.
P.Brian.Mackey

3

큰 객체와 긴밀하게 결합 된 코드 가 적절하고 더 나은 엔지니어링이 필요한 경우에는 아무런 문제가 없습니다 . 이것은 엄지 손가락 교리로 바뀌는 규칙의 또 다른 표현입니다 .

느슨한 결합과 작고 간단한 물체는 많은 경우에 특정한 이점을 제공하는 경향이 있으므로 일반적으로 사용하는 것이 좋습니다. 문제는 적용하지 않는 곳에서도 원칙적으로 맹목적으로 적용하려는 원칙의 근거를 이해하지 못하는 사람들에게 있습니다.


1
+1. "적절한"의 의미에 대한 올바른 정의가 여전히 필요합니다.
jprete

2
@jprete : 왜요? 절대 정의를 적용하는 것은 이와 같은 개념적 혼란 의 원인 중 하나입니다. 좋은 소프트웨어를 만드는 데는 많은 장인 정신이 있습니다. 실제로 필요한 것은 경험과 올바른 판단입니다.
메이슨 휠러

4
예,하지만 어떤 종류 의 광범위한 정의 는 여전히 유용합니다.
jprete

1
적절한 정의를 갖는 것이 유용 할 것이지만, 그것이 요점입니다. 사례별로 만 적용 할 수 있기 때문에 소리에 고정하기가 어렵습니다. 각 사례에서 적절한 선택을하는지 여부는 크게 경험하기 어렵습니다. 왜 응집력이 높은지 명확하게 설명 할 수 없다면, 모 놀리 식 솔루션은 응집력이 낮고 모듈 식 솔루션보다 낫습니다. 항상 리팩토링하는 것이 더 쉽습니다.
Ian

1
나는 맹목적으로 스파게티 코드를 작성하는 대신 맹목적으로 분리 된 코드를 작성하는 사람을 선호합니다 :)
Boris Yankov

2

나는 당신이 필요로하지 않을 것이라는 관점에서 이것에 더 접근하는 경향이 있습니다. 이 게시물은 특히 상속이라는 한 항목에 중점을 둡니다.

초기 디자인에서는 두 가지 이상이있을 것으로 알고있는 것의 계층 구조를 만듭니다. 그들은 동일한 코드가 많이 필요할 것이므로 처음부터 그것을 설계 할 가치가 있습니다. 초기 코드가 설치되고 더 많은 기능을 추가해야하는 경우, 내가 가지고있는 것을보고 "이미 동일한 기능 또는 비슷한 기능을 구현 한 것이 있습니까?"라고 생각합니다. 그렇다면 새로운 추상화가 공개되기를 바랄 것입니다.

이에 대한 좋은 예는 MVC 프레임 워크입니다. 처음부터 시작하여 코드 뒤에 하나의 큰 페이지를 만듭니다. 그러나 다른 페이지를 추가하고 싶습니다. 그러나 하나의 코드 뒤에 이미 새 페이지에 필요한 많은 논리가 구현되어 있습니다. 따라서 해당 페이지에 고유Controller 한 논리를 구현 하는 클래스 / 인터페이스 를 추상화 하여 일반적인 "신"코드 숨김에 공통적 인 내용을 남겨 둡니다.


1

코드의 미래로 인해 원칙을 적용하지 않을 때 개발자로 알고 있다면 도움이 될 것입니다.

언급 한 값에서 성능이 저하되기 시작하면 복잡성을 피하고 코드 품질을 향상시키기 위해 상황을 추상화해야하는 시점을 알고 SOLID가 마음에 남아 있기를 바랍니다.

더 중요한 것은 대부분의 개발자가 하루 종일 일하고 있고 당신만큼 신경 쓰지 않는다는 것을 고려하십시오. 처음에 오래 실행되는 메소드를 설정했다면 코드를 사용하는 다른 개발자는 코드를 유지하거나 확장 할 때 어떻게 생각할까요? ... 예, 더 큰 기능, 더 긴 실행 클래스 등 신 개체, 그들은 성장하는 코드를 잡고 올바르게 캡슐화 할 수있는 원칙을 염두에두고 있지 않습니다. "읽을 수있는"코드가 이제 썩어 버립니다.

따라서 나쁜 개발자가 관계없이 그렇게 할 것이라고 주장 할 수도 있지만, 처음부터 일이 단순하고 잘 정리되어 있으면 적어도 더 나은 이점이 있습니다.

글로벌 한 "레지스트리 맵"이나 "하나님의 개체"가 단순하다면 특별히 신경 쓰지 않습니다. 그것은 실제로 시스템 설계에 달려 있습니다. 때로는 그것을 빠져 나가서 단순하게 유지할 수 있습니다.

또한 큰 기능과 신 물체는 테스트하기가 매우 어렵다는 것을 잊지 마십시오. 코드를 리팩토링하고 변경할 때 민첩성을 유지하고 "안전"하다고 느끼려면 테스트가 필요합니다. 함수 나 객체가 많은 일을 할 때 테스트를 작성하기가 어렵습니다.


1
기본적으로 좋은 답변입니다. 내 생각에 일부 유지 보수 개발자가 와서 혼란을 겪으면 리팩토링하지 않는 것이 잘못입니다. 그러한 변경 사항이 계획을 정당화하기에 충분한 예측력이 없거나 코드가 잘못 문서화 / 테스트되지 않은 경우 등 . 리팩토링은 광기의 한 형태 일 것입니다.
dsimcha

그건 사실 dsimcha입니다. 개발자가 "RequestHandlers"폴더를 생성하고 해당 폴더 아래의 각 클래스가 요청 처리기 인 시스템에서 생각한 다음 개발자는 "새로운 요청 처리기를 넣어야합니다"라고 생각합니다. 현재의 인프라 스트럭처는 그를 따라 대회를 따라 가도록 강요합니다. 나는 이것이 내가 말하려고하는 것이라고 생각합니다. 처음부터 명확한 컨벤션을 설정하면 가장 경험이 부족한 개발자도 패턴을 계속 진행할 수 있습니다.
Martin Blore

1

신 개체의 문제는 일반적으로 수익성있게 조각으로 나눌 수 있다는 것입니다. 정의상 그것은 하나 이상의 일을합니다. 더 작은 클래스로 나누는 전체 목적은 한 가지 일을 잘 수행하는 클래스를 가질 수 있도록하는 것입니다 (그리고 "한 가지"의 정의를 확장해서는 안됩니다). 즉, 주어진 수업에 대해해야 할 일을 알면 상당히 쉽게 읽고 한 일을 올바르게 수행하고 있는지 말할 수 있어야합니다.

내가 거기 생각 입니다 너무 많은 모듈화, 너무 많은 유연성을 가지고와 같은 일이,하지만의 더 오버 디자인과 오버 엔지니어링 문제의 어디에서도 고객이 원하는 모르는 요구 사항에 대한 당신있는 거 회계. 코드가 어떻게 실행되는지에 대한 변경을 쉽게 제어 할 수 있도록 많은 디자인 아이디어가 지향되지만 변경을 기대하는 사람이 없다면 유연성을 포함시키는 것은 의미가 없습니다.


작업중인 추상화 수준에 따라 "둘 이상의 작업을 수행"하는 것은 정의하기 어려울 수 있습니다. 두 줄 이상의 코드가있는 메소드는 둘 이상의 작업을 수행한다고 주장 할 수 있습니다. 스펙트럼의 반대쪽 끝에서, 스크립팅 엔진과 같은 복잡한 것은 모두 서로 직접 관련이없는 개별 "사물"을 수행하지만 각각 "메타- "스크립트가 제대로 실행되게하고 스크립트 엔진을 손상시키지 않고 할 수있는 많은 것들이 있습니다.
메이슨 휠러

개념적인 수준의 스펙트럼이 분명히 있지만 우리 그 점을 선택할 수 있습니다 . "한 가지 일을하는 클래스"의 크기는 구현을 거의 즉시 이해할 수있는 크기 여야합니다. 즉, 구현 세부 사항이 머리에 맞습니다. 더 커지면 한 번에 전체 수업을 이해할 수 없습니다. 더 작 으면 너무 추상화 된 것입니다. 그러나 다른 의견에서 말했듯이 많은 판단과 경험이 필요합니다.
jprete

IMHO 최고의 추상화 수준은 객체 소비자가 사용할 것으로 예상되는 수준입니다. Queryer데이터베이스 쿼리를 최적화하고 RDBMS를 쿼리하며 결과를 개체로 구문 분석하여 반환 하는 개체가 있다고 가정합니다 . 앱 에이 작업을 수행 할 수있는 Queryer방법이 하나만 있고 밀폐 되어이 방법을 캡슐화하면 한 가지 작업 만 수행합니다. 이 작업을 수행하는 여러 가지 방법이 있고 누군가가 한 부분의 세부 사항을 신경 쓰면 여러 가지 작업을 수행하고 분할 할 수 있습니다.
dsimcha

1

더 간단한 지침을 사용합니다. 단위 테스트를 작성할 수 있고 코드 복제가없는 경우에는 충분히 추상적입니다. 또한 나중에 리팩토링 할 수있는 좋은 위치에 있습니다.

그 외에도 SOLID를 명심해야하지만 규칙보다는 지침으로 사용해야합니다.


0

모듈성이 더 필요하지 않고 중복이없고 코드를 읽을 수 있다면 God 객체와 밀접하게 결합 된 코드에 어떤 문제가 있는가?

응용 프로그램이 충분히 작 으면 무엇이든 유지할 수 있습니다. 더 큰 응용에서 신의 대상은 빨리 문제가된다. 결국 새로운 기능을 구현하려면 17 개의 God 객체를 수정해야합니다. 사람들은 17 단계 절차를 잘 따르지 않습니다. God 객체는 여러 개발자가 지속적으로 수정하고 있으며 이러한 변경 사항은 반복적으로 병합해야합니다. 당신은 거기 가고 싶지 않아.


0

과도하고 부적절한 추상화에 대한 귀하의 우려를 공유하지만, 조기 추상화에 대해 반드시 그렇게 걱정하지는 않습니다.

아마도 모순처럼 들릴 수 있지만 추상화를 사용하면 너무 일찍 커밋하지 않는 한, 즉 필요에 따라 리팩토링 할 수있는 한 문제가 발생하지 않을 것입니다.

이것이 의미하는 것은 코드에서 어느 정도의 프로토 타이핑, 실험 및 역 추적입니다.

즉, 모든 문제를 해결할 간단한 결정적 규칙은 없습니다. 경험은 많지만 실수를함으로써 그 경험을 얻어야합니다. 그리고 이전의 실수가 당신을 준비시키지 않았을 수 있도록 더 많은 실수가 항상 있습니다.

그럼에도 불구하고 교과서 원칙을 출발점으로 취급 한 다음 프로그래밍 로트를 배우고 이러한 원리가 어떻게 작동하는지 살펴보십시오. SOLID와 같은 것보다 더 정확하고 신뢰할 수있는 지침을 제공 할 수 있었다면 누군가가 지금 그렇게했을 것입니다. 비록 그랬더라도 개선 된 원칙은 여전히 ​​불완전 할 것이며 사람들은 그 한계에 대해 묻고있을 것입니다 .

프로그램을 설계하고 코딩하기 위해 명확하고 결정론적인 알고리즘을 제공 할 수 있다면, 인간이 작성할 마지막 프로그램은 하나뿐입니다.이 프로그램은 인간의 개입없이 모든 미래 프로그램을 자동으로 작성합니다.


0

적절한 추상화 선을 그리는 것은 경험에서 얻은 것입니다. 그렇게 할 때 실수를 저지르는 것은 부인할 수없는 일입니다.

SOLID는 어려운 경험을 가진 사람들이 제공하는 집중된 경험입니다. 추상화를 필요로하기 전에 추상화 생성을 삼가겠다고 말했을 때 거의 그것을 못 박았습니다. SOLID가 제공하는 경험은 전혀 새로운 문제 해결하는 데 도움이됩니다 . 그러나 나를 믿어 라. .. 진짜가있다.

SOLID는 모듈성, 모듈성은 유지 관리 성, 유지 관리 성은 소프트웨어가 생산에 도달하고 고객이없는 버그를 발견하면 인간적인 작업 일정을 유지할 수있게하는 것입니다.

모듈성의 가장 큰 장점은 테스트 가능성입니다. 시스템이 모듈 식 일수록 테스트하기가 쉬우 며 문제의 어려운 부분에 대처할 수있는 견고한 기반을 더 빨리 구축 할 수 있습니다. 따라서 문제는이 모듈성을 요구하지 않을 수도 있지만 더 나은 테스트 코드를 더 빨리 만들 수 있다는 사실만으로 모듈화를 위해 노력할 필요는 없습니다.

민첩성을 유지하는 것은 빠른 배송과 좋은 품질 제공 간의 미묘한 균형을 맞추는 것입니다. 애자일은 우리가 모퉁이를 줄여야한다는 것을 의미하지는 않습니다. 사실, 제가 참여한 가장 성공적인 애자일 프로젝트는 그러한 가이드 라인에주의를 기울인 프로젝트입니다.


0

간과 된 것으로 보이는 중요한 점은 SOLID가 TDD와 함께 연습된다는 것입니다. TDD는 특정 상황에서 "적절한"것이 무엇인지 강조하고 조언에있는 것처럼 보이는 많은 속임수를 완화하는 데 도움이됩니다.


1
답을 넓히는 것을 고려하십시오. 현재로서는 두 개의 직교 개념을 통합하고 있으며 결과가 OP의 문제를 어떻게 해결하는지 명확하지 않습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.