Recentemente, li a postagem no blog Three Big Lies e estou tendo dificuldades para justificar a segunda mentira, citada aqui:
(MENTIRA # 2) O CÓDIGO DEVE SER PROJETADO EM TORNO DE UM MODELO DO MUNDO
Não há valor no código ser algum tipo de modelo ou mapa de um mundo imaginário. Não sei por que este é tão atraente para alguns programadores, mas é extremamente popular. Se houver um foguete no jogo, tenha certeza de que existe uma classe "Foguete" (supondo que o código seja C ++) que contém dados para exatamente um foguete e faz coisas perigosas. Sem levar em consideração o que realmente está sendo feito na transformação de dados ou o layout dos dados. Ou, nesse caso, sem o entendimento básico de que onde há uma coisa, provavelmente há mais de uma.
Embora existam muitas penalidades de desempenho para esse tipo de design, a mais significativa é que ela não é dimensionada. Em absoluto. Cem foguetes custam cem vezes mais que um foguete. E é extremamente provável que custe ainda mais do que isso! Mesmo para quem não é programador, isso não deve fazer sentido. Economia de escala. Se você tem mais de algo, deve ficar mais barato, não mais caro. E a maneira de fazer isso é projetar os dados corretamente e agrupar as coisas por transformações semelhantes.
Aqui estão meus problemas com essa mentira em particular.
Existe valor no código ser um modelo / mapa de um mundo imaginário, pois a modelagem do mundo imaginário ajuda (pelo menos eu, pessoalmente) a visualizar e organizar o código.
Ter uma aula "Rocket" é, para mim, uma escolha perfeitamente válida para uma aula. Talvez "foguetes" possam ser divididos em tipos de foguetes como AGM-114 Hellfire, etc., que conteriam força de carga, velocidade máxima, raio máximo de rotação, tipo de mira e assim por diante, mas ainda assim todos os foguetes disparados precisariam ter uma posição e uma velocidade.
É claro que ter 100 Rockets custa mais de 1 Rocket. Se houver 100 Rockets na tela, deve haver 100 cálculos diferentes para atualizar sua posição. O segundo parágrafo parece afirmar que, se houver 100 Rockets, custará menos de 100 cálculos para atualizar o estado?
Meu problema aqui é que o autor apresenta um modelo de programação "defeituoso", mas não apresenta uma maneira de "corrigi-lo". Talvez eu esteja explorando a analogia da classe Rocket, mas eu realmente gostaria de entender o raciocínio por trás dessa mentira. Qual é a alternativa?
fonte
Respostas:
Primeiramente, vejamos algum contexto: este é um designer de jogos escrevendo em um blog cujo assunto está obtendo a última gota de desempenho de uma CPU Cell BE. Em outras palavras: trata-se de programação de jogos de console, mais especificamente, programação de jogos de console para o PlayStation 3.
Agora, os programadores de jogos são um grupo curioso, os programadores de jogos de console ainda mais, e o Cell BE é uma CPU bastante estranha. (Há uma razão pela qual a Sony adotou um design mais convencional para o PlayStation 4!)
Então, temos que olhar para essas afirmações dentro deste contexto.
Existem também algumas simplificações nessa postagem do blog. Em particular, essa mentira nº 2 é mal apresentada.
Eu argumentaria que tudo o que abstrai do mundo real é um modelo em algum sentido. E como o software não é real, mas virtual, é sempre uma abstração e, portanto, sempre um modelo. Mas! Um modelo não precisa ter um mapeamento 1: 1 limpo para o mundo real. Afinal, isso é o que o torna um modelo em primeiro lugar.
Então, em certo sentido, o autor está claramente errado: software é um modelo. Período. Em algum outro sentido, ele está certo: esse modelo não precisa se parecer com o mundo real.
Vou dar um exemplo que já dei em algumas outras respostas ao longo dos anos, o (in) famoso exemplo de introdução à conta bancária OO 101. Veja como é uma conta bancária em quase todas as classes OO:
Então: o
balance
é de dados , etransfer
é uma operação .Mas! Veja como é uma conta bancária em quase todos os softwares bancários de todos os tempos:
Então, agora
transfer
são dados ebalance
são uma operação (uma dobra à esquerda sobre o log de transações). (Você também notará queTransactionSlip
é imutável,balance
é uma função pura,TransactionLog
pode ser uma estrutura de dados "quase" imutável somente para acréscimo ... Tenho certeza de que muitos de vocês viram os erros de simultaneidade flagrantes na primeira implementação, que agora desaparecem magicamente .)Observe que esses dois são modelos. Ambos são igualmente válidos. Ambos estão corretos. Ambos modelam a mesma coisa. E, no entanto, eles são exatamente duplos entre si: tudo o que são dados em um modelo é uma operação no outro modelo, e tudo o que é uma operação em um modelo é dados no outro modelo.
Portanto, a questão não é se você modela o "mundo real" em seu código, mas como você o modela.
Como se vê, o segundo modelo é, na verdade, também como o sistema bancário funciona no mundo real. Como eu sugerido acima, este segundo modelo é principalmente imutável e puro, e imune a erros de simultaneidade, que é realmente muito importante se considerarmos que houve um tempo não muito tempo atrás, em que
TransactionSlip
s eram reais pedaços de papel que foram enviadas ao redor via cavalo e carruagem.No entanto, o fato de esse segundo modelo realmente corresponder ao funcionamento do sistema bancário real e ao funcionamento do software bancário do mundo real não o torna automaticamente mais "certo". Porque, na verdade, o primeiro modelo ("errado") se aproxima bastante da maneira como os clientes bancários veem seu banco. Para eles ,
transfer
é uma operação (eles precisam preencher um formulário) ebalance
é uma parte dos dados na parte inferior do extrato da conta.Assim, ele pode muito bem ser verdade que no código do motor núcleo jogo de um jogo de tiro PS3 de alto desempenho, não haverá um
Rocket
tipo, mas ainda assim, haverá alguma modelagem do mundo acontecendo, mesmo se o modelo parece estranho para alguém que não é especialista no domínio da programação de mecanismos de física de jogos de console.fonte
balance
como dados e transações como mais dados e será transferido como operações, porque é isso que o usuário vê, mesmo que o back-end possa tratá-lo de maneira diferente.Eu discordo de toda "mentira" que ele propõe.
TL; DR O autor deste artigo estava tentando ser controverso para tornar seu artigo mais interessante, mas as chamadas "mentiras" são aceitas pelos desenvolvedores de software por boas razões.
Mentira # 1 - Big O é importante para fins de dimensionamento. Ninguém se importa se um aplicativo minúsculo demora mais tempo e é a única constante de tempo que importa, eles se importam que, quando dobram o tamanho da entrada, não multiplicam o tempo de execução por um fator de 10.
Mentira # 2 - A modelagem de programas após o mundo real permite que um programador que analisa seu código três anos depois entenda facilmente o que está fazendo. O código precisa ser de manutenção ou você precisará passar horas apenas tentando entender o que o programa está tentando fazer. Outra resposta sugeriu que você pode ter mais classes genéricas como
LaunchPad
eMassiveDeviceMover
. Não é necessariamente uma classe ruim, mas você ainda precisaria daRocket
classe. Como alguém deve saber o queMassiveDeviceMover
faz ou o que se move? Está movendo montanhas, naves espaciais ou planetas? Isso basicamente significa que adicionar classes comoMassiveDeviceMover
torna seu programa menos eficiente (mas possivelmente muito mais legível e compreensível).Além disso, o custo do tempo do desenvolvedor começou a exceder o custo do hardware há muito tempo. É uma idéia horrível começar tentando projetar com otimização na frente de seus pensamentos. Você programá-lo da maneira fácil e compreensível e depois ajustá-lo depois de descobrir quais partes de seus programas estão demorando muito tempo para serem executadas. Não se esqueça: 80% do tempo de execução está sendo usado por 20% do programa.
Mentira # 3 - O código é extremamente importante. Um código bem escrito (e modular) permite a reutilização (economizando inúmeras horas de trabalho). Ele também permite que você examine e reconheça dados ruins para que possam ser manipulados. Os dados são maravilhosos, mas sem o código, seria impossível analisar e obter informações úteis.
fonte
Em um sistema de comércio eletrônico, você não lida com "foguetes" em nível de classe, mas com "produtos". Portanto, depende do que você está tentando realizar e do seu nível de abstração desejado.
Em um jogo, pode-se argumentar que os foguetes são apenas um dos muitos tipos de "objetos em movimento". A mesma física se aplica a eles como a todos os outros objetos em movimento. Portanto, no mínimo, o "foguete" herdará de uma classe base mais geral de "objeto em movimento".
De qualquer forma, o autor da passagem que você citou parece ter exagerado um pouco o caso. Os foguetes ainda podem ter características únicas, como "quantidade de combustível restante" e "empuxo", e você não precisa de cem classes para representá-lo para cem foguetes, precisa apenas de um. A criação de objetos é um custo bastante baixo na maioria das linguagens de programação decentes; portanto, se você precisar rastrear coisas semelhantes a foguetes, a noção de que você não deve criar objetos Rocket porque pode ser muito caro não faz muito sentido.
fonte
O problema com o mundo real é toda essa maldita física. Separamos as coisas em objetos físicos no mundo real porque são mais fáceis de mover do que átomos individuais, ou uma escória gigante derretida de algo que pode ser um foguete.
Da mesma forma, o mundo real fornece vários recursos úteis nos quais confiamos. É realmente fácil fazer exceções do Penguin - "todos os pássaros voam, exceto ...". E é realmente fácil rotular as coisas como foguetes, quero dizer, se eu chamar esse pinguim de foguete e acendê-lo ... simplesmente não funciona.
Então, como separamos as coisas no mundo real funciona conceitualmente sob essas restrições. Quando fazemos coisas no código, devemos separar as coisas para que funcionem bem sob essas restrições, que são decididamente diferentes.
Pense em redes. Não modelamos portas, fios e roteadores no código. Em vez disso, abstraímos a comunicação de rede em conexões e protocolos. Fazemos isso porque é uma abstração útil, independentemente da implementação no mundo real. E coloca restrições úteis (por exemplo: você não pode se comunicar até que a conexão seja aberta) que só importam no código .
Então, sim, às vezes modelar código depois que o mundo real funciona, mas isso é uma coincidência . Quando as pessoas falam sobre POO, os objetos não são objetos do mundo real. Que escolas e tutoriais digam o contrário é uma tragédia de décadas.
fonte
Rocket
tipo de código desse cara, eu estou disposto a apostar que há, contudo, algum modelo de ...A alternativa é modelar as coisas com as quais seus programas se importam. Mesmo que seu programa lide com foguetes, talvez você não precise de uma entidade chamada a
Rocket
. Por exemplo, você pode ter umaLaunchPad
entidade e umaLaunchSchedule
entidade e umaMassiveDeviceMover
entidade. O fato de tudo isso ajudar o lançamento de foguetes não significa que você esteja lidando com eles.fonte
Esse é o verdadeiro problema, mas eu darei a você como desenvolvedor, talvez isso ajude.
Primeiro, eu não chamaria isso de mentira, como equívocos comuns. Chamar isso de mentira é apenas um hype.
Um Ele está certo, de certa forma. Não vou gastar muito tempo nisso, porque isso não faz parte da questão. Mas, em essência, ele está correto. Eu poderia reafirmar isso como "O que funciona em um laboratório pode não funcionar na vida real". Muitas vezes os desenvolvedores aderem a um design que funciona em um "laboratório", mas falha em aplicativos do mundo real.
Três Soa um pouco quadradão para mim, mas essencialmente ele está correto novamente. Mas isso pode ser reescrito para "escrever código de acordo com suas necessidades, não tente ajustá-las ao seu código".
Dois Novamente, aqui ele está correto. Vi desenvolvedores passarem semanas ou mais desenvolvendo uma classe "foguete" quando uma simples classe "veículo" funcionaria, ou uma classe ainda mais simples, "móvel". Se tudo o que seu foguete precisa fazer é mover-se do lado esquerdo da tela para a direita e emitir um som, você poderá usar a mesma classe que usou para carros, trens, barcos e moscas. Os 100 devem custar menos que 1 * 100 argumento parece estar no tempo gasto desenvolvendo, e não tanto em custos de computação. Embora aderir a menos classes gerais que sejam reutilizadas seja "mais barato", muitas classes específicas que não podem ser reutilizadas. Provavelmente, isso pode ser reescrito porque "as classes gerais são melhores do que as classes específicas,
Em essência, o artigo inteiro poderia ser reescrito, com menos chavões e teria apenas um parágrafo na melhor das hipóteses. Dito isto, é um post de blog focado em uma área estreita de programação. Fiz alguma programação incorporada e posso concordar com a idéia geral por trás dessas declarações, embora exista um pouco de "fluff" ao redor delas para torná-la adequada para uma apresentação na GDC.
Uma última nota, o artigo foi escrito em 2008 (o melhor que eu poderia dizer). As coisas mudam rapidamente. As afirmações são verdadeiras hoje, mas os sistemas embarcados são muito mais comuns hoje e naquela época, e os padrões de desenvolvimento mudam. Talvez até em resposta a este artigo / conversa.
fonte
Acho interessante que estas se concentrem em preocupações acadêmicas: a plataforma, a eficiência do uso da memória e os dados. Mas ignora completamente o elemento humano.
Software é atender às necessidades das pessoas. Normalmente, isso é quantificado em termos de negócios - existem clientes que desejam algo e apoiadores que estão dispostos a pagar para que isso aconteça. Se o software está sendo escrito de forma a atender às necessidades de ambos os lados da equação, então é um software bom; se não, é um software ruim.
Se a plataforma não é importante para o cliente, a plataforma não é importante. Se a eficiência da memória não é importante para o cliente, não é importante. Se os dados não são importantes para o cliente, eles não são importantes. Se o código funcionar, mas não puder ser lido ou mantido, e o cliente desejar alterações rápidas e confiáveis por um preço razoável, um código mal escrito é uma coisa ruim. Se o código funciona, mas não pode ser lido ou mantido, e o cliente não se importa ou está disposto a pagar por refatores caros, um código mal escrito é uma coisa boa.
A grande mentira é que qualquer coisa, menos o elemento humano, é importante. Por que os dados são importantes? Porque há algum cliente ou parte interessada que precisa ser. Essa é a "grande verdade".
fonte
IMHO Se o código é "projetado em torno de um modelo do mundo", é mais fácil entender, tanto para designers quanto para desenvolvedores e para os mantenedores. Mas acho que não sou só eu, e não apenas software. Wikipedia: Modelagem científica é uma atividade científica, cujo objetivo é tornar uma parte ou característica específica do mundo mais fácil de entender, definir, quantificar, visualizar ou simular, referenciando-a ao conhecimento existente e geralmente aceito
fonte