거짓말 2 : 코드는 세계 모델을 중심으로 설계되어야합니까? [닫은]

최근에 Three Big Lies 블로그 게시물을 읽었으며 두 번째 거짓말을 정당화하는 데 어려움을 겪고 있습니다.

(LIE # 2) 코드는 세계의 모델에 따라 설계되어야한다

코드는 상상의 세계에 대한 일종의 모델이나 맵이 아닙니다. 나는 이것이 왜 일부 프로그래머들에게 그렇게 매력적인 지 모르겠지만 매우 인기가 있습니다. 게임에 로켓이있는 경우 정확히 하나의 로켓에 대한 데이터를 포함하고 로켓을 수행하는 “로켓”클래스 (코드가 C ++라고 가정)가 있는지 확인하십시오. 어떤 데이터 변환이 실제로 수행되는지 또는 데이터 레이아웃에 대해서는 전혀 고려되지 않습니다. 또는 그 문제에 대해, 한 가지가있는 곳에는 아마도 둘 이상이 있다는 기본적인 이해없이.

이러한 종류의 디자인에는 많은 성능 저하가 있지만 가장 중요한 것은 확장이 불가능하다는 것입니다. 조금도. 100 개의 로켓은 1 개의 로켓보다 100 배나 비싸다. 그리고 그것보다 훨씬 많은 비용이 듭니다! 프로그래머가 아닌 사람도 말이되지 않습니다. 규모의 경제. 더 많은 것을 가지고 있다면 더 비싸지 않고 저렴해야합니다. 이를 수행하는 방법은 데이터를 올바르게 설계하고 유사한 변환으로 항목을 그룹화하는 것입니다.

이 거짓말에 대한 내 문제는 특히 있습니다.

  1. 상상의 세계를 모델링하면 (적어도 개인적으로) 코드를 시각화하고 구성하는 데 도움이되기 때문에 상상의 세계의 모델 /지도 인 코드에는 가치가 있습니다.

  2. “로켓”수업을받는 것은 수업을위한 완벽한 선택입니다. 아마도 “로켓”은 AGM-114 Hellfire와 같은 유형의 로켓으로 분류 될 수 있으며, 여기에는 페이로드 강도, 최대 속도, 최대 회전 반경, 타겟팅 유형 등이 포함되지만 여전히 발사되는 모든 로켓은 위치를 가져야합니다. 그리고 속도.

  3. 물론 100 로켓은 1 로켓 이상이 든다. 화면에 100 개의 로켓이있는 경우 위치를 업데이트하려면 100 개의 서로 다른 계산이 있어야합니다. 두 번째 단락은 100 개의 로켓이 있다면 상태를 업데이트하는 데 100 회 미만의 계산이 필요하다는 주장을하는 것처럼 들립니다.

여기서 문제는 저자가 “결함이있는”프로그래밍 모델을 제시하지만 그것을 “수정”하는 방법을 제시하지 않는다는 것입니다. 아마도 나는 Rocket 클래스의 비유에 대해 생각하고 있지만 실제로이 거짓말의 이유를 이해하고 싶습니다. 대안은 무엇입니까?



답변

먼저, 문맥을 보자. 이것은 블로그에서 글을 쓰는 게임 디자이너로서 Cell BE CPU의 성능 저하를 막고있다. 다시 말해, 콘솔 게임 프로그래밍, 특히 PlayStation 3의 콘솔 게임 프로그래밍에 관한 것입니다.

이제 게임 프로그래머는 호기심이 많고 콘솔 게임 프로그래머는 훨씬 더 많으며 Cell BE는 다소 이상한 CPU입니다. (Sony가 PlayStation 4를 위해보다 전통적인 디자인을 사용하는 이유가 있습니다!)

따라서 우리는이 맥락에서 그러한 진술을 살펴 봐야합니다.

해당 블로그 게시물에도 일부 단순화가 있습니다. 특히,이 거짓말 # 2는 잘 제시되지 않았습니다.

나는 실제 세계에서 추상화되는 모든 것이 어떤 의미에서 모델 이라고 주장합니다 . 그리고 소프트웨어는 실제가 아니라 가상이기 때문에 항상 추상화이므로 항상 모델입니다. 그러나! 모델은 실제 세계에 1 : 1로 깔끔하게 매핑 할 필요가 없습니다. 즉, 결국 모델을 모델로 만드는 것입니다.

어떤 의미에서 저자는 분명히 틀렸다 : 소프트웨어 모델이다. 기간. 다른 의미에서 그는 옳습니다. 그 모델은 실제로 실제 세계와 전혀 닮을 필요는 없습니다.

나는 OO 101 은행 계좌에 대한 (in) 유명한 소개 예제와 같이 이미 몇 년 동안 다른 답변을 한 예를 들겠습니다. 거의 모든 OO 클래스에서 은행 계좌는 다음과 같습니다.

class Account {
  var balance: Decimal
  def transfer(amount: Decimal, target: Account) = {
    balance -= amount
    target.balance += amount
  }
}

그래서 다음은 balance이다 데이터transfer입니다 작업 .

그러나! 거의 모든 은행 소프트웨어에서 은행 계좌는 다음과 같습니다.

class TransactionSlip {
  val transfer(amount: Decimal, from: Account, to: Account)
}

class Account {
  def balance =
    TransactionLog.filter(t => t.to == this).map(_.amount).sum -
    TransactionLog.filter(t => t.from == this).map(_.amount).sum
}

그래서, 지금 transfer이다 데이터balance입니다 작업은 (a는 트랜잭션 로그를 통해 배 왼쪽). (또한 TransactionSlip불변이고 balance순수한 함수이며 TransactionLog추가 전용 “거의”불변 데이터 구조가 될 수 있습니다 … 첫 번째 구현에서 눈부신 동시성 버그를 발견했습니다. 이제 마술처럼 사라졌습니다. .)

하는 것으로 이 두 모델입니다. 둘 다 동일하게 유효합니다. 둘 다 맞습니다. 이 두 모델은 같은 것입니다. 그럼에도 불구하고 그것들은 서로 정확히 이중 입니다. 한 모델의 데이터 인 모든 것은 다른 모델의 작업이고, 한 모델의 연산 인 모든 것은 다른 모델의 데이터입니다.

따라서 문제는 코드에서 “실제 세계”를 모델링 하는지 여부 가 아니라 모델링하는 방법 입니다.

결과적으로 두 번째 모델은 실제로 은행 업무가 실제 세계에서 어떻게 작동하는지입니다. 나는이 두 번째 모델은 실제로 당신이 시간이 너무 오래 전에 여기서이없는 것을 고려한다면 매우 중요 동시성 버그에 주로 불변과 순수, 그리고 면역 위의 암시로 TransactionSlip들이었다 실제 주위에 전송 된 종이 전표 말과 마차를 통해.

그러나이 두 번째 모델이 실제 뱅킹의 작동 방식과 실제 뱅킹 소프트웨어의 작동 방식과 실제로 일치한다고해서 자동으로 어떻게 더 “올바른”것이되지는 않습니다. 실제로 첫 번째 ( “잘못된”) 모델은 은행 고객이 자신의 은행을 보는 방식과 거의 비슷하기 때문 입니다. 에 , transfer입니다 작업 (그들은 양식을 작성해야합니다), 그리고 balance의 작품이다 데이터를 자신의 계좌 명세서의 맨 아래에.

따라서 고성능 PS3 슈팅 게임의 핵심 게임 엔진 코드에는 Rocket유형이 없지만 모델이 이상하게 보일지라도 여전히 세계의 일부 모델링이 진행될 것입니다. 콘솔 게임 물리 엔진 프로그래밍 분야의 전문가가 아닌 사람에게.


답변

나는 그가 제안한 모든 “거짓말”에 동의하지 않습니다.

TL; DR 이 기사의 저자는 기사를 더 흥미롭게 만들기 위해 논쟁의 여지가 있었지만 소위 “거짓말”은 정당한 이유로 소프트웨어 개발자들에게 받아 들여졌습니다.

거짓말 # 1-Big O는 스케일링 목적에 중요합니다. 작은 응용 프로그램에 시간 상수가 중요한 유일한 시간이 걸리더라도 아무도 신경 쓰지 않습니다. 입력 크기를 두 배로 늘릴 때 실행 시간에 10 배가 곱해지지 않습니다.

거짓말 # 2-실제 환경 이후의 프로그램 모델링을 통해 프로그래머는 3 년 후 코드를보고있는 프로그램이 수행중인 작업을 쉽게 이해할 수 있습니다. 코드는 유지 관리 가능해야하거나 프로그램이 수행하려는 작업을 이해하기 위해 몇 시간을 소비해야합니다. 또 다른 대답은 LaunchPadand와 같은보다 일반적인 클래스를 가질 수 있다고 제안했습니다 MassiveDeviceMover. 이것들은 반드시 나쁜 수업이 아니지만 반드시 수업이 필요합니다 Rocket. 누구가 어떻게 행동하는지 MassiveDeviceMover또는 어떻게 움직이는 지 어떻게 알 수 있습니까? 움직이는 산, 우주선 또는 행성입니까? 이것은 기본적으로 클래스를 추가 MassiveDeviceMover하면 프로그램의 효율성이 떨어지지 만 읽을 수 있고 이해하기 쉽다는 것을 의미합니다 .

또한 개발자 시간 비용이 오래 전에 하드웨어 비용을 초과하기 시작했습니다. 생각 앞에서 최적화로 디자인하려고 시도하는 것이 끔찍한 아이디어입니다. 쉽고 이해하기 쉬운 방식으로 프로그래밍 한 다음 실행하는 데 많은 시간이 걸리는 프로그램 부분을 찾은 후 프로그램을 조정하십시오. 잊지 마십시오 : 실행 시간의 80 %가 프로그램의 20 %에 의해 사용되고 있습니다.

거짓말 # 3-코드가 매우 중요합니다. 잘 작성된 (그리고 모듈 식) 코드를 통해 재사용 성을 높일 수 있습니다 (수 많은 인력 시간 절약). 또한 불량 데이터를 선별하고 인식하여 처리 할 수 ​​있습니다. 데이터는 훌륭하지만 코드가 없으면 유용한 정보를 분석하고 얻는 것이 불가능합니다.


답변

전자 상거래 시스템에서는 클래스 레벨에서 “로켓”을 처리하지 않고 “제품”을 처리합니다. 따라서 달성하려는 목표와 원하는 추상화 수준에 따라 다릅니다.

게임에서 로켓은 여러 유형의 “이동 물체”중 하나 일 뿐이라고 주장 할 수 있습니다. 다른 모든 움직이는 물체와 동일한 물리학이 적용됩니다. 최소한 “로켓”은 좀 더 일반적인 “움직이는 객체”기본 클래스에서 상속됩니다.

어쨌든 당신이 인용 한 구절의 저자는 그의 사건을 약간 과장 한 것 같습니다. 로켓은 여전히 ​​”남은 연료의 양”과 “추력”과 같은 고유 한 특성을 가질 수 있으며, 100 개의 로켓에 대해 이것을 나타 내기 위해 100 개의 클래스가 필요하지 않으며 하나만 필요합니다. 대부분의 괜찮은 프로그래밍 언어에서는 객체 생성이 상당히 저렴하므로 로켓과 같은 것을 추적해야하는 경우 로켓 객체를 너무 비싸서 만들지 말아야한다는 개념은 의미가 없습니다.


답변

현실 세계의 문제는 물리학이 저 버리는 것입니다. 우리는 물체가 개별 원자보다 이동하기 쉬우므로 잠재적으로 로켓이 될 수있는 거대한 녹은 슬래그보다 실제 세계의 물체로 분리합니다.

마찬가지로, 실제 세계는 우리가 의존하는 여러 유용한 기능을 제공합니다. 펭귄 예외를 만드는 것은 정말 쉽습니다. “…을 제외한 모든 새들은 날아갑니다.” 그리고 물건을 로켓으로 표시하는 것은 정말 쉽습니다. 펭귄을 로켓이라고 부르고 불을 붙이면 작동하지 않습니다.

우리가 현실 세계에서 사물을 분리하는 방법은 개념적 으로 그러한 제약 하에서 작동 합니다. 우리는 코드에서 일을 할 때, 우리는 잘 아래 작업에 일을 분리해야 하는 확실히 다른 제약.

대안은 무엇입니까?

네트워크에 대해 생각하십시오. 우리는 코드로 포트와 와이어 및 라우터를 모델링하지 않습니다. 대신 네트워크 통신을 연결 및 프로토콜로 추상화합니다. 우리는 실제 구현에 관계없이 유용한 추상화이기 때문에 그렇게합니다. 또한 코드 에서만 중요한 유용한 제약 조건 (예 : 연결이 열릴 때까지 통신 할 수 없음)이 있습니다 .

그렇습니다. 때로는 실제 환경이 작동 한 후 코드를 모델링하는 경우도 있지만 우연일치 입니다. 사람들이 OOP에 대해 이야기 할 때 객체는 실제 객체가 아닙니다. 그 학교와 튜토리얼은 그렇지 않으면 수십 년의 비극이라고 말합니다.


답변

대안은 프로그램이 중요하게 생각하는 것을 모델링하는 것입니다. 프로그램이 로켓을 다루더라도라는 엔티티가 필요하지 않을 수 있습니다 Rocket. 예를 들어 LaunchPad엔터티와 LaunchSchedule엔터티 및 엔터티 가있을 수 있습니다 MassiveDeviceMover. 이 모든 것이 로켓 발사에 도움이된다고해서 로켓 자체를 다루는 것은 아닙니다.


답변

여기서 문제는 저자가 “결함이있는”프로그래밍 모델을 제시하지만 그것을 “수정”하는 방법을 제시하지 않는다는 것입니다. 아마도 나는 Rocket 클래스의 비유에 대해 생각하고 있지만 실제로이 거짓말의 이유를 이해하고 싶습니다. 대안은 무엇입니까?

이것은 실제 문제이지만 개발자로 데려다 줄 것입니다. 아마 도움이 될 것입니다.

첫째, 나는 일반적인 오해로 거짓말을하지 않을 것입니다. 거짓말이라고하는 것은 과장된 일입니다.

하나 그는 어떤면에서는, 권리입니다. 이것에 많은 시간을 할애하지 않으면 질문의 일부가 아닙니다. 그러나 본질적으로 그는 정확합니다. 이것을 “실험실에서 작동하는 것이 실생활에서 작동하지 않을 수 있습니다”라고 다시 말할 수 있습니다. 개발자가 너무 자주 “랩”에서 작동하지만 실제 응용 프로그램에서는 실패하는 디자인을 고수합니다.

Three 소리가 조금 비눗물이지만, 본질적으로 그는 다시 맞습니다. 그러나 “필요에 맞게 코드를 작성하고 요구에 맞게 코드를 작성하지 마십시오”라고 다시 작성할 수 있습니다.

다시 번, 그는 정확합니다. 간단한 “차량”클래스가 작동하거나 더 간단한 “이동식”클래스가 작동 할 때 개발자가 몇 주 이상 “로켓”클래스를 개발하는 것을 보았습니다. 로켓이 화면의 왼쪽에서 오른쪽으로 이동하여 소리를내는 것이라면 자동차, 기차, 보트 및 파리와 같은 클래스를 사용할 수 있습니다. 100은 1 * 100보다 적은 비용이 듭니다. 개발에 소요되는 시간은 계산 비용에 그리 많지 않습니다. 재사용되는 일반 클래스가 적을수록 “더 저렴”하지만 재사용 할 수없는 많은 특정 클래스가 있습니다. “일반 클래스가 특정 클래스보다 낫습니다.

본질적으로, 전체 기사는 더 적은 유행어로 다시 쓰여질 수 있으며, 가장 긴 단락 일뿐입니다. 즉, 좁은 프로그래밍 영역에 중점을 둔 블로그 게시물입니다. 나는 임베디드 프로그래밍을 해왔고, GDC에서의 프리젠 테이션에 적합하도록 그들 주위에 약간의 “fluff”가 있지만,이 문장 뒤에있는 일반적인 아이디어에 동의 할 수있다.

마지막으로,이 기사는 2008 년에 작성되었습니다. 상황이 빠르게 바뀝니다. 그 진술은 오늘날에도 사실이지만 임베디드 시스템은 그 당시 훨씬 더 일반적이며 개발 패턴이 변경됩니다. 아마도이 기사 / 이야기에 대한 응답으로도.


답변

이러한 것들이 플랫폼, 메모리 사용 효율성 및 데이터와 같은 학문적 관심의 중심에 있다는 점이 흥미 롭습니다. 그러나 그것은 인간 요소를 완전히 무시합니다.

소프트웨어는 사람들의 요구를 충족시키는 것입니다. 일반적으로 이것은 비즈니스 용어로 수량화됩니다. 무언가를 원하는 고객과 후원자가 기꺼이 지불하려고합니다. 소프트웨어가 방정식의 양측의 요구를 충족시키는 방식으로 작성된다면, 소프트웨어는 좋지만 그렇지 않은 경우 나쁜 소프트웨어입니다.

플랫폼이 고객에게 중요하지 않은 경우 플랫폼이 중요하지 않습니다. 메모리 효율성이 고객에게 중요하지 않은 경우 중요하지 않습니다. 데이터가 고객에게 중요하지 않은 경우 데이터는 중요하지 않습니다. 코드가 작동하지만 읽거나 유지 관리 할 수없고 고객이 적절한 가격으로 빠르고 안정적인 변경을 원하는 경우 잘못 작성된 코드는 나쁜 것입니다. 코드가 작동하지만 읽거나 유지 관리 할 수없고 고객이 값 비싼 리 팩터에 신경 쓰지 않거나 기꺼이 지불하려는 경우 코드를 잘못 작성하는 것이 좋습니다.

가장 큰 거짓말은 인간의 요소 이외의 모든 것이 중요합니다. 왜 데이터가 중요합니까? 필요한 고객이나 이해 관계자가 있기 때문입니다. 이것이 “큰 진실”입니다.