Lie 2: il codice dovrebbe essere progettato attorno a un modello del mondo? [chiuso]

23

Recentemente ho letto il post del blog Three Big Lies e sto avendo un momento difficile che giustifica la seconda bugia, che è citato qui:

(LIE #2) CODE SHOULD BE DESIGNED AROUND A MODEL OF THE WORLD

There is no value in code being some kind of model or map of an imaginary world. I don't know why this one is so compelling for some programmers, but it is extremely popular. If there's a rocket in the game, rest assured that there is a "Rocket" class (Assuming the code is C++) which contains data for exactly one rocket and does rockety stuff. With no regard at all for what data tranformation is really being done, or for the layout of the data. Or for that matter, without the basic understanding that where there's one thing, there's probably more than one.

Though there are a lot of performance penalties for this kind of design, the most significant one is that it doesn't scale. At all. One hundred rockets costs one hundred times as much as one rocket. And it's extremely likely it costs even more than that! Even to a non-programmer, that shouldn't make any sense. Economy of scale. If you have more of something, it should get cheaper, not more expensive. And the way to do that is to design the data properly and group things by similar transformations.

Ecco i miei problemi con questa menzogna in particolare.

  1. Il valore del codice è rappresentato da un modello / mappa di un mondo immaginario mentre la modellazione del mondo immaginario aiuta (almeno io, personalmente) a visualizzare e organizzare il codice.

  2. Avere una classe "Rocket" è, per me, una scelta perfettamente valida per una classe. Forse i "Rocket" potrebbero essere suddivisi in tipi di Rockets come AGM-114 Hellfire, ecc. Che potrebbero contenere forza di carico utile, velocità massima, raggio di virata massimo, tipo di puntamento e così via, ma comunque ogni razzo dovrebbe avere una posizione e una velocità.

  3. Naturalmente avere 100 missili costa più di 1 razzo. Se ci sono 100 missili sullo schermo ci devono essere 100 calcoli diversi per aggiornare la loro posizione. Il secondo paragrafo suona come se affermasse che se ci sono 100 missili, dovrebbe costare meno di 100 calcoli per aggiornare lo stato?

Il mio problema qui è che l'autore presenta un modello di programmazione "imperfetto" ma non presenta un modo per "correggerlo". Forse sto inciampando sull'analogia della classe Rocket, ma mi piacerebbe davvero capire il ragionamento dietro questa bugia. Qual è l'alternativa?

    
posta thndrwrks 07.09.2016 - 18:45
fonte

8 risposte

63

In primo luogo, diamo un'occhiata ad un contesto: questo è un designer di giochi che scrive su un blog il cui soggetto sta emettendo l'ultima goccia di prestazioni da una CPU di Cell BE. In altre parole: si tratta della programmazione del gioco per console, in particolare della programmazione per console per PlayStation 3.

Ora, i programmatori di giochi sono un gruppo curioso, i programmatori di videogiochi ancora di più, e la cella BE è una CPU piuttosto strana. (C'è un motivo per cui Sony è andato con un design più convenzionale per la PlayStation 4!)

Quindi, dobbiamo esaminare quelle dichiarazioni in questo contesto.

Ci sono anche alcune semplificazioni in quel post sul blog. In particolare, questa Lie # 2 è presentata male.

Direi che tutto che astrae dal mondo reale è un modello in un certo senso. E poiché il software non è reale, ma virtuale, è sempre un'astrazione e quindi sempre un modello. Ma! Un modello non deve avere una mappatura 1: 1 pulita sul mondo reale. Dopotutto, ciò che lo rende un modello in primo luogo.

Quindi, in un certo senso, l'autore ha chiaramente torto: il software è un modello. Periodo. In qualche altro senso, ha ragione: quel modello in realtà non deve assomigliare affatto al mondo reale.

Darò un esempio che ho già dato in altre risposte nel corso degli anni, la (in) famosa Introduzione all'esempio di conto bancario OO 101. Ecco come appare un conto bancario in quasi tutte le classi OO di sempre:

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

Quindi: balance è data e transfer è un'operazione .

Ma! Ecco come appare un conto bancario in quasi tutti i software bancari di sempre:

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
}

Quindi ora transfer è data e balance è un'operazione (una piega a sinistra sul log delle transazioni). (Si noterà anche che TransactionSlip è immutabile, balance è una funzione pura, TransactionLog può essere una "quasi" immutabile datastructure di sola append ... Sono sicuro che molti di voi hanno individuato i vistosi bug della concorrenza in la prima implementazione, che ora magicamente scompare.)

Si noti che entrambi questi sono modelli. Entrambi sono ugualmente validi. Entrambi sono corretti. Entrambi questi modelli sono la stessa cosa. Eppure, sono esattamente doppi tra loro: tutto ciò che è dati in un modello è un'operazione nell'altro modello e tutto ciò che è un'operazione in un modello sono dati nell'altro modello.

Quindi, la domanda non è se si modella il "mondo reale" nel codice, ma come lo si modella.

A quanto pare, il secondo modello è in realtà anche il modo in cui il sistema bancario funziona nel mondo reale. Come ho accennato sopra, questo secondo modello è per lo più immutabile e puro, e immune da bug di concorrenza, che è in realtà molto importante se si considera che c'è stato un tempo non troppo tempo fa, dove TransactionSlip s erano reali foglietti che sono stati spediti in giro via cavallo & trasporto.

Tuttavia, il fatto che questo secondo modello corrisponda effettivamente sia al funzionamento delle operazioni bancarie nel mondo reale sia a come funziona il vero software bancario mondiale, non lo rende automaticamente più "giusto". Perché, in realtà, il modello primo ("sbagliato") si avvicina abbastanza strettamente a come i clienti bancari vedono la loro banca. Per loro , transfer è un'operazione (devono compilare un modulo) e balance è un pezzo di dati a la parte inferiore dell'estratto conto.

Quindi, potrebbe benissimo essere vero che nel codice del motore di gioco core di uno sparatutto PS3 ad alte prestazioni, non ci sarà un tipo Rocket , ma comunque ci saranno alcuni la modellazione del mondo in corso, anche se il modello sembra strano a qualcuno che non è un esperto nel campo della programmazione dei motori fisici di console.

    
risposta data 07.09.2016 - 21:59
fonte
19

Non sono d'accordo con ogni "bugia" che propone.

TL; DR L'autore di questo articolo stava cercando di essere controverso per rendere il loro articolo più interessante, ma le cosiddette "bugie" sono accettate dagli sviluppatori di software per buone ragioni.

Lie # 1 - Big O è importante per scopi di ridimensionamento. A nessuno importa se una piccola applicazione impiega più tempo, il che è l'unica costante di tempo, a loro importa che raddoppiando la dimensione dell'ingresso non moltiplichi il tempo di esecuzione di un fattore di 10.

Lie # 2 - I programmi di modellazione dopo il mondo reale consentono a un programmatore di guardare il tuo codice 3 anni dopo per capire facilmente cosa sta facendo. Il codice deve essere mantenibile o occorrerebbe passare ore a cercare semplicemente di capire che cosa sta tentando di fare il programma. Un'altra risposta suggeriva che puoi avere più classi generiche come LaunchPad e MassiveDeviceMover . Non è necessariamente una cattiva classe, ma avresti comunque bisogno della classe Rocket . Come si può supporre che qualcuno sappia cosa fa MassiveDeviceMover o cosa si muove? Muove montagne, astronavi o pianeti? Ciò significa fondamentalmente che l'aggiunta di classi come MassiveDeviceMover rende il tuo programma meno efficiente (ma probabilmente più leggibile e comprensibile).

Inoltre, il costo del tempo di sviluppo ha iniziato a superare il costo dell'hardware molto tempo fa. È un'idea orribile iniziare a provare a progettare con l'ottimizzazione in primo piano. Lo programmate in modo facile e comprensibile e poi modificate il vostro programma dopo aver scoperto quali parti dei vostri programmi impiegano molto tempo per essere eseguite. Non dimenticare: l'80% del tempo di esecuzione viene utilizzato dal 20% del programma.

Lie # 3 - Il codice è estremamente importante. Il codice ben scritto (e modulare) consente la riutilizzabilità (risparmiando innumerevoli ore lavorative). Permette anche di setacciare e riconoscere i dati non validi in modo che possano essere gestiti. I dati sono meravigliosi, ma senza il codice sarebbe impossibile analizzare e ottenere informazioni utili da esso.

    
risposta data 07.09.2016 - 20:03
fonte
6

In un sistema di e-commerce, non ti occupi di "razzi" a livello di classe, ti occupi di "prodotti". Quindi dipende da cosa stai cercando di realizzare e dal tuo livello di astrazione desiderato.

In un gioco, si potrebbe sostenere che i razzi sono solo uno dei tanti tipi di "oggetti in movimento". La stessa fisica si applica a loro come a tutti gli altri oggetti in movimento. Quindi, per lo meno, "il razzo" erediterà da una classe di base "oggetto mobile" più generale.

In ogni caso, l'autore del brano che hai citato sembra aver esagerato un po 'il suo caso. I razzi possono ancora avere caratteristiche uniche, come "quantità di carburante rimanente" e "spinta", e non hai bisogno di cento classi per rappresentare questo per cento razzi, ne hai solo bisogno. La creazione di oggetti è abbastanza economica nella maggior parte dei linguaggi di programmazione decenti, quindi se hai bisogno di tracciare cose simili a razzi, l'idea che non dovresti creare oggetti Rocket perché potrebbe essere troppo costosa non ha molto senso.

    
risposta data 07.09.2016 - 18:59
fonte
5

Il problema con il mondo reale è tutta quella dannata fisica. Separiamo le cose in oggetti fisici nel mondo reale perché sono più facili da spostare rispetto ai singoli atomi o una gigantesca scoria fusa di qualcosa che potrebbe potenzialmente essere un razzo.

Allo stesso modo, il mondo reale fornisce una serie di funzioni utili su cui facciamo affidamento. È davvero facile fare eccezioni Penguin: "tutti gli uccelli volano, eccetto ...". Ed è davvero facile etichettare le cose come razzi, voglio dire se io chiamo quel pinguino un razzo e lo accendo ... semplicemente non funziona.

Quindi, come separiamo le cose nel mondo reale concettualmente funziona sotto questi vincoli. Quando facciamo cose in codice, dovremmo separare le cose per lavorare bene sotto i quei vincoli, che sono decisamente diversi.

What is the alternative?

Pensa alle reti. Non modelliamo porte, fili e router nel codice. Al contrario, estraiamo la comunicazione di rete in connessioni e protocolli. Lo facciamo perché è un'astrazione utile indipendentemente dall'implementazione nel mondo reale. E pone vincoli utili (es: non puoi comunicare fino a quando la connessione non viene aperta) che importa solo nel codice .

Quindi sì, a volte il codice di modellazione dopo che il mondo reale funziona, ma questa è una coincidenza . Quando le persone parlano di OOP, gli oggetti non sono oggetti del mondo reale. Che le scuole e i tutorial dicano il contrario è una tragedia lunga decenni.

    
risposta data 07.09.2016 - 21:36
fonte
2

L'alternativa è modellare le cose a cui i tuoi programmi si interessano. Anche se il tuo programma si occupa di missili, potresti non aver bisogno di avere un'entità chiamata Rocket . Ad esempio, potresti avere un'entità LaunchPad e un'entità LaunchSchedule e un'entità MassiveDeviceMover . Il fatto che tutto questo serva a lanciare i razzi non significa che tu stia gestendo i razzi stessi.

    
risposta data 07.09.2016 - 18:54
fonte
0

My problem here is that the author presents a "flawed" programming model but doesn't present a way to "correct" it. Perhaps I'm tripping up on the analogy of the Rocket class, but I would really like to understand the reasoning behind this lie. What is the alternative?

Questo è il vero problema, ma ti darò il mio incarico come sviluppatore, forse questo mi aiuterà.

In primo luogo, non definirei alcuna bugie, come idee sbagliate comuni. Chiamarlo bugie è solo esagerare.

Uno Ha ragione, in qualche modo. Non passerò molto tempo su questo, perché non fa parte della domanda. Ma in sostanza ha ragione. Potrei ribadire che "ciò che funziona in un laboratorio potrebbe non funzionare nella vita reale". Troppe volte gli sviluppatori si attengono a un progetto che funziona in un "laboratorio" ma non funziona nelle applicazioni del mondo reale.

Tre Suona un po 'di sapone per me, ma essenzialmente è di nuovo corretto. Ma questo potrebbe essere riscritto per "scrivere codice in base alle tue esigenze, non provare a soddisfare le esigenze del tuo codice."

Due Di nuovo, qui è corretto. Ho visto sviluppatori spendere settimane o più a sviluppare una classe "razzo" quando una semplice classe "veicolo" avrebbe funzionato, o una classe "mobile" ancora più semplice. Se tutto ciò che il tuo razzo deve fare è spostarti dal lato sinistro dello schermo verso destra e fare un suono, quindi puoi usare la stessa classe che hai fatto per auto, treni, barche e mosche. Il 100 dovrebbe costare meno di 1 * 100 argomento sembra essere nel tempo speso nello sviluppo, e non tanto nei costi di calcolo. Anche se attenersi a un numero minore di classi generali che è possibile riutilizzare è "più economico", quindi molte classi specifiche che non possono essere riutilizzate. Probabilmente questo potrebbe essere riscritto perché "le classi generali sono migliori rispetto a classi specifiche, quando l'unica ragione per la classe più specifica è quella di incontrare un modello del mondo reale su come funzionano le cose."

In sostanza, l'intero articolo potrebbe essere riscritto, con meno parole d'ordine e sarebbe solo un paragrafo lungo al massimo. Detto questo, si tratta di un post sul blog incentrato su un'area ristretta di programmazione. Ho fatto alcune programmazioni incorporate, e posso essere d'accordo, con l'idea generale alla base di queste affermazioni, anche se c'è un po 'di "fluff" attorno a loro per renderlo adatto per una presentazione a GDC.

Un'ultima nota, l'articolo è stato scritto nel 2008 (il meglio che potrei dire). Le cose cambiano rapidamente. Oggi le affermazioni sono vere, ma oggi i sistemi incorporati sono molto più comuni e i modelli di sviluppo cambiano. Forse anche in risposta a questo articolo / talk.

    
risposta data 07.09.2016 - 22:35
fonte
-1

Trovo interessante il fatto che queste bugie siano incentrate su preoccupazioni accademiche: la piattaforma, l'efficienza dell'uso della memoria e i dati. Ma ignora completamente l'elemento umano.

Il software tratta di soddisfare le esigenze delle persone. In genere questo è quantificato in termini commerciali: ci sono clienti che vogliono qualcosa e sostenitori disposti a pagare per realizzarlo. Se il software viene scritto in modo da soddisfare le esigenze di entrambi i lati dell'equazione, allora è un buon software, in caso contrario, si tratta di software non valido.

Se la piattaforma non è importante per il cliente, la piattaforma non è importante. Se l'efficienza della memoria non è importante per il cliente, non è importante. Se i dati non sono importanti per il cliente, i dati non sono importanti. Se il codice funziona, ma non può essere letto o mantenuto, e il cliente desidera modifiche rapide e affidabili a un prezzo accettabile, allora il codice scritto male è una cosa negativa. Se il codice funziona, ma non può essere letto o mantenuto, e il cliente non si cura o è disposto a pagare per costosi refactories, allora il codice scritto male è una buona cosa.

La grande bugia è che tutto ciò che conta è l'elemento umano. Perché i dati sono importanti? Perché c'è un cliente o uno stakeholder che ha bisogno di essere. Questa è la "grande verità".

    
risposta data 07.09.2016 - 19:19
fonte
-1

IMHO Se il codice è "disegnato attorno a un modello del mondo" è più facile da capire, sia per i progettisti e gli sviluppatori, sia per i manutentori. Ma penso che non sono solo io, e non solo il software. Da Wikipedia: La modellistica scientifica è un'attività scientifica, il cui scopo è quello di rendere una particolare parte o caratteristica del mondo più facile da capire, definire, quantificare, visualizzare o simulare facendo riferimento alla conoscenza esistente e solitamente comunemente accettata

    
risposta data 07.09.2016 - 19:44
fonte

Leggi altre domande sui tag