Todo objeto deve saber se apresentar / desenhar?

8

David West, em seu livro Object Thinking (capítulo 10, seção 1, subseção 2), propôs que, em um ambiente ideal de OO, todos os objetos deveriam ser capazes de se apresentar mediante solicitação; seja para humanos (como GUI), componentes não nativos (como JSON e / ou XML) ou qualquer outra parte interessada:

O pensamento sobre objetos diz que uma visão (às vezes chamada de interface) - gráfica ou não - é um meio para um objeto se comunicar com outro objeto e nada mais. A necessidade de uma visualização surge quando um objeto precisa se apresentar de forma "não nativa" para outro objeto (geralmente um ser humano) ou aplicativo (por exemplo, uma visualização XML para objetos de dados compartilhados entre plataformas).

A descoberta da necessidade e dos parâmetros que devem ser satisfeitos por uma exibição é manifestada nos cenários em que o objeto participa. Sempre que um objeto é solicitado a se exibir, ele deve usar uma exibição - uma representação - apropriada para o remetente dessa mensagem de exibição. Se, por exemplo, um objeto está tentando instanciar-se (obter um valor para si mesmo), ele deve apresentar uma visão de si mesmo como uma solicitação implícita a um ser humano (ou outro objeto de prestação de serviço) por um valor. Se estivermos criando uma GUI que servirá como intermediário entre um objeto de software e um objeto humano, usaremos glifos para fins de exibição e widgets para fins de interação.

Mas quais glifos e widgets precisam ser incluídos na GUI? Somente aqueles necessários para concluir o cenário ou os cenários 4 de interesse imediato à medida que o aplicativo é executado. Essa perspectiva é contra-intuitiva para a maioria dos desenvolvedores, pois sugere que uma GUI seja definida a partir do aplicativo.

Como exemplo, considere uma cervejaria. De um lado estão cubas cheias de cerveja. Na complexa linha de produção que consiste em lavadoras de garrafas, estações de envase, tampadoras e montadoras de embalagens. Acima de tudo, há uma estação de controle que monitora a cervejaria e notifica os gerentes humanos sobre status e problemas. É provável que os desenvolvedores tradicionais iniciem sua análise e design de "um sistema de gerenciamento de cervejaria" do ponto de vista do painel de controle. Isso é análogo ao design a partir da interface no.

O pensamento de objetos sugere, em vez disso, que você considere qual objeto é o principal cliente da cervejaria e de todas as suas inúmeras máquinas. Em nome de quem existe o complexo labirinto de equipamentos? A resposta correta dos negócios é, obviamente, "O cliente". Mas uma resposta mais reflexiva do pensamento sobre objetos é: "A cerveja". Todos os cenários são escritos da perspectiva da cerveja, tentando entrar em uma garrafa, com uma tampa, colocada em um pacote e residente em um caminhão. O painel de controle é um observador passivo 5 do estado da cervejaria. Se a cerveja encontrar um problema em algum momento, é responsabilidade da cerveja solicitar intervenção dos operadores humanos enviando uma mensagem ao painel de controle (ou painéis de controle específicos da máquina) solicitando um serviço de intervenção.

Essa perspectiva simplificará o design da GUI e, mais importante, eliminará o host de objetos de gerente e controlador que parecem surgir inevitavelmente ao projetar a partir da perspectiva do painel de controle (GUI).

Vindo de um iniciante no mundo OO: será esse realmente o caso?

Ter objetos que sabem se representar certamente poderia reduzir o número de objetos de controlador / gerente que West disse repetidamente em seu livro que um Object Thinker supostamente deveria tentar evitar a todo custo. Mas o cumprimento dessa "regra" não quebrará o SRP ?

Além disso (se for o caso), dada uma implementação típica em, digamos, um aplicativo Android: Como alguém pode atingir esse tipo de objetivo? Todo objeto que criamos deve saber se apresentar como um View?

MrHadiSatrio
fonte
3
SRP não significa o que você pensa que significa. SRP significa que o seu objeto "carro" não diagnostica problemas com animais de estimação. Isso não significa que um elemento visual da interface do usuário não seja capaz de se exibir.
Robert Harvey
Eu vejo. No entanto, você menciona o elemento visual da interface do usuário . Agora, se aplicarmos essa regra ao projetar nosso software, a maioria dos objetos que criaremos não será elementos visuais da interface do usuário ? Como a maioria deles exigirá uma interação com o usuário mais cedo ou mais tarde, certo?
MrHadiSatrio
1
@RobertHarvey Não tenho certeza de como essa citação pode ser vista como compatível com um padrão do tipo MVC. Parece que está dizendo que é responsabilidade da classe definir sua interface / "visualização" para atender às suas necessidades, enquanto que em um padrão MVC, o modelo ignoraria a visualização que o exibe.
Ben Aaronson
1
@ridsatrio Como você é um iniciante auto-admitido ... sugiro que você se esforce para escrever interfaces que existem para contar a um objeto algo que aconteceu fora de seu controle, em vez de métodos que instruem objetos a fazerem coisas. Portanto, métodos como "buttonWasClicked" ou "pageWillBeDisplayed" são melhores que "changeState" ou "drawInCorner". "Diga não pergunte ..." conte aos objetos o que está acontecendo, em vez de pedir que eles façam as coisas.
Daniel T.
2
Em certo momento, o autor deveria ter parado de escrever e pensou: "Eu realmente quero ensinar as pessoas sobre OOP falando sobre cerveja que quer estar em uma garrafa e dizendo aos operadores quando não pode?" Não vejo como isso deve iluminar qualquer conceito ou idéia para alguém.
Sebastian Redl 30/03

Respostas:

12

Acho que essa é uma das coisas mais difíceis de entender sobre o design de OO e, honestamente, acho que muitos autores estão errados sobre isso e / ou não o explicam muito bem. Muitas pessoas entendem isso errado e nunca descobrem realmente. Vamos dar um exemplo que não é baseado em GUI, mas que fica na mesma armadilha.

Em Java, todo objeto tem um método igual. Você tem tipos de coleções como set e map que dependem desse método para determinar quando os objetos devem ser adicionados à coleção ou quando são duplicados. Isso parece ser bom para muitas pessoas. O problema é que você acaba com um objeto (a coleção) cujo comportamento não é determinado por ele, mas pelos objetos que ele contém. É um pouco como ter os passageiros no ônibus direto para onde deveriam ir. E se eles discordarem? Este não é um problema teórico, é um problema realmente complicado, onde você basicamente precisa quebrar a herança para evitar erros no seu programa. Tome uma forma e uma forma colorida. Um quadrado 2x2 é igual a um quadrado azul 2x2? Shape diz 'yes' e ColoredShape diz 'no'. Quem' está certo? A resposta depende do que você deseja que aconteça na sua coleção. Pode não depender nem do que você está tentando fazer.

Você verá isso surgir como um problema repetidamente. O engraçado é que existe uma solução e fica ao lado do Comparable. Os objetos que implementam Comparable têm esse mesmo dilema, mas agora eles precisam não apenas determinar se são iguais, mas também se são maiores que outro objeto. É realmente intratável fora de um escopo muito restrito de uso. Então, temos outra coisa chamada Comparador. Seu trabalho é olhar para dois objetos e dizer à coleção qual é o maior. Todos os problemas que você está tentando fazer isso no objeto comparável desaparecem.

Não conheço este livro e não conheço o autor, mas o exemplo da cerveja não parece útil. Como a cerveja saberia se deveria estar em uma garrafa ou em um barril e por que tomaria essa decisão? Seu trabalho é provar bem e fornecer álcool à corrente sanguínea dos usuários. Realmente achamos que as cervejarias funcionam assim? "Ok cerveja, você deve estar em uma garrafa ou em um barril e se for uma garrafa, deve ser uma garrafa de 25 onças ou uma garrafa de 12 onças?" Qual é a cerveja neste caso (sem trocadilhos) afinal? É uma gota de cerveja? Talvez isso esteja fora de contexto, mas acho que isso está errado ou, pelo menos, não está adicionando nenhuma iluminação a esse conceito.

Dito tudo isso, há uma abordagem para a criação de interfaces que usei que pode simplificar as coisas e torná-las mais OO. Essencialmente, você cria uma interface que define as ações abstratas que você pode executar para exibir o objeto. Você pode ter uma interface chamada Displaymétodos como setTitleou setDescriptionse estiver usando o padrão de nomenclatura Java padrão. Então seu objeto teria um métododisplay(Display display)(porque três vezes é o charme!) Nessa abordagem, o objeto não precisa entender qual é a interface, pode ser texto, binário, svg, bitmap, o que for e a interface não precisa saber sobre o objeto . Dessa maneira, um objeto pode "se exibir" sem precisar saber como o monitor funciona. Essa abordagem pode reduzir bastante o número de classes de wrapper necessárias, mas pode ser complicada se você tiver requisitos de exibição complexos que variam de acordo com o objeto. Você pode misturá-lo com abordagens padrão do tipo MVC com bom efeito.

JimmyJames
fonte
2
Boa resposta, e acho que toca em todas as questões relevantes.
Robert Harvey
Esta é realmente uma resposta muito agradável. Venha para pensar sobre isso, talvez sua maneira de permitir que objetos se desenhem através de uma interface definida seja a melhor abordagem para esse assunto. Dessa forma, os objetos ainda têm o direito de controlar como desejam ser exibidos (não por um controlador externo), mas sem a necessidade de aprender como um Displaytrabalho funcionaria em sua implementação concreta (como deveriam JFrame, por exemplo, por exemplo) ) Obrigado.
MrHadiSatrio
1
Esta resposta é incrível e me faz refletir muito sobre o POO em geral. Eu achei um ECS muito mais fácil de manter, que separa os dados e a funcionalidade, e em parte porque ele não sofre com a analogia do que você descreveu usando objetos tentando determinar internamente qual é o comportamento correto para comparar-se a algo mais.
8

O princípio da responsabilidade única não significa que uma classe faça apenas uma coisa. Isso significa que uma classe tem um único motivo para mudar.

Você provavelmente está pensando em um método , que realmente faz apenas uma coisa.

Levada à sua conclusão lógica, sua versão do SRP significaria que você nunca seria capaz de registrar nada, porque o registro é uma responsabilidade separada.

É melhor pensar em uma classe como um assunto único e bem definido . Você pode ter vários métodos que apóiam esse assunto, e todos podem estar fazendo coisas diferentes.

O método mais fundamental "exibir-me" é ToString, que é sempre um método no objeto.


Tudo isso dito, quando criamos uma interface do usuário, geralmente incentivamos a Separação de Preocupações , fornecendo objetos cujo único objetivo é exibir dados de outros objetos (por exemplo, Visualizações).

Talvez um exemplo esteja em ordem. Considere um site PHP, usando um View. Uma visualização simples pode ser assim:

<?php
class View
{
    private $model;
    private $controller;

    public function __construct($controller,$model) {
        $this->controller = $controller;
        $this->model = $model;
    }

    public function output(){
        return "<p>" . $this->model->string . "</p>";
    }
}

No PHP, o View sempre contém uma output()função. Para obter a representação visual de uma visualização, basta ligar output()e você obterá uma string adequada para exibição em qualquer navegador moderno.

Se você perceber, a tela faz referência a um objeto chamado model. Este é o objeto que contém os dados reais. A vista e o modelo estão em objetos separados; é isso que estabelece a separação de preocupações.

A separação de preocupações é importante para sites, porque é o que permite que um designer trabalhe em um design de página da Web, além do programador.

Leitura Adicional
O Padrão MVC e PHP

Robert Harvey
fonte
Bem. Mas depois que separamos a visualização do modelo, ainda é tarefa do modelo fornecer a visualização? Porque é isso que estou obtendo da citação acima: o objeto (ou modelo no seu vocabulário) deve saber como se exibir, certo? Em certo sentido, digamos que você tenha um Receiptobjeto em um software típico de PDV, você ainda ligaria Receipt.draw(Canvas)e não ReceiptView.draw(Receipt).
precisa saber é o seguinte
2
No MVC, é de responsabilidade do Controlador escolher qual visualização usar.
Robert Harvey
Mas o objeto dos controladores é exatamente o que está sendo colocado como algo a ser evitado na percepção de West no Object Thinking (e, portanto, nessa citação). A citação afirma explicitamente que toda a idéia por trás de "objetos que sabem se exibir" é evitar "objetos de gerente e controlador".
MrHadiSatrio 29/03
Tudo bem, mas acho que você terá que limitar esse pensamento a objetos cujo único objetivo é a exibição visual. Visualizações, em outras palavras. O controlador não se importa como a exibição é exibida; só se preocupa com qual visualização usar. Além disso, o MVC não é o único paradigma de exibição possível e outros (principalmente o MVVM) funcionam de maneira um pouco diferente. Mas o princípio básico da Separação de Preocupações permanece.
Robert Harvey
Observe que o livro que você está lendo é bastante antigo para um livro de software (2004). O MVC e o MVVM ainda não haviam sido utilizados em comum.
Robert Harvey
1

A julgar pelas citações coladas, você está interpretando mal o texto.

O autor não está dizendo que todo objeto deve ser capaz de se apresentar em uma interface do usuário. Isso seria impossível, pois um objeto não pode saber em qual interface do usuário ele será mostrado (em um aplicativo WinForms, um aplicativo Linux usando XServer, como uma string JSON, como XML, como uma imagem PNG etc.).

O argumento é que você deve escrever visões especializadas cuja única responsabilidade é exibir uma determinada entidade. Você pode, por exemplo, escrever uma exibição que renderize um objeto como HTML (como é feito pelas exibições nos aplicativos MVC). Você pode criar um serializador JSON que pode transformar um objeto em uma sequência JSON. Você pode criar um objeto que transforme outros tipos de objetos em relatórios PDF. Tudo depende.

A questão é separar a entidade comercial da representação visual (ou serializada). São coisas diferentes e mudam por diferentes razões.

sara
fonte
Eu editei minha pergunta para elaborar a citação.
MrHadiSatrio
1
Embora admita que minha resposta não pareça mais tão relevante, não vejo o que sua pergunta tem a ver com a citação. A citação parece estar falando mais em termos de APIs e interfaces, em vez de renderização visual. Quais partes de um objeto devem estar acessíveis e quais mensagens ele pode enviar para concluir seu trabalho no domínio comercial?
sara
"Se estivermos criando uma GUI que servirá como intermediário entre um objeto de software e um objeto humano, usaremos glifos para fins de exibição e widgets para fins de interação". A julgar por essa frase, tenho certeza de que a citação também implica propósitos de renderização visual.
MrHadiSatrio 29/03
Não há nada afirmando que "o objeto em questão deve escolher quais glifos e widgets que melhor o representam para um determinado contexto e também deve ser responsável por renderizar os referidos glifos e widgets". O objeto e a coisa que a exibe não são necessariamente (não deveriam ser) a mesma coisa.
sara