Por exemplo, suponha que você tenha um programa de jogo de console, que possui todos os tipos de métodos de entrada / saída de e para o console. Seria inteligente para mantê-los todos em uma única inputOutput
classe ou quebrá-los para mais classes específicas como startMenuIO
, inGameIO
, playerIO
, gameBoardIO
, etc. de forma que cada classe tem cerca de 1-5 métodos?
E na mesma nota, se for melhor decompô-los, seria inteligente colocá-los em um IO
espaço para nome, tornando-os um pouco mais detalhados, por exemplo: IO.inGame
etc.?
Respostas:
Atualização (recapitulação)
Desde que eu escrevi uma resposta bastante detalhada, aqui está o que tudo se resume a:
inGameIO
e asplayerIO
classes provavelmente constituiriam uma violação do SRP. Provavelmente significa que você está acoplando a maneira como lida com as E / S com a lógica do aplicativo.Eu acho que você está vendo isso da maneira errada. Você está separando o IO em função dos componentes do aplicativo, enquanto - para mim - faz mais sentido ter classes de IO separadas com base na origem e no "tipo" de IO.
Tendo algumas
KeyboardIO
classes básicas / genéricasMouseIO
para começar e, depois, com base em quando e onde você precisar delas, tenha subclasses que lidam com o referido IO de maneira diferente.Por exemplo, a entrada de texto é algo que você provavelmente deseja manipular de maneira diferente dos controles do jogo. Você vai querer mapear determinadas chaves de maneira diferente, dependendo de cada caso de uso, mas esse mapeamento não faz parte do próprio IO, é como você está lidando com o IO.
Seguindo o SRP, eu teria algumas classes que posso usar para E / S de teclado. Dependendo da situação, provavelmente desejarei interagir com essas classes de maneira diferente, mas o único trabalho deles é me dizer o que o usuário está fazendo.
Eu injetava esses objetos em um objeto manipulador que mapeia a IO bruta para algo com o qual minha lógica de aplicativo possa trabalhar (por exemplo: o usuário pressiona "w" , o manipulador mapeia isso
MOVE_FORWARD
).Esses manipuladores, por sua vez, são usados para fazer os personagens se moverem e desenharem a tela de acordo. Uma simplificação grosseira, mas a essência é esse tipo de estrutura:
O que temos agora é uma classe responsável pelo IO do teclado em sua forma bruta. Outra classe que traduz esses dados em algo que o mecanismo do jogo pode realmente entender, esses dados são usados para atualizar o estado de todos os componentes envolvidos e, finalmente, uma classe separada cuidará da saída da tela.
Cada classe tem um único trabalho: o tratamento da entrada do teclado é feito por uma classe que não sabe / se importa / precisa saber o significado da entrada que está processando. Tudo o que faz é saber como obter a entrada (em buffer, sem buffer, ...).
O manipulador converte isso em uma representação interna para o restante do aplicativo, a fim de entender essas informações.
O mecanismo de jogo pega os dados que foram traduzidos e os utiliza para notificar todos os componentes relevantes de que algo está acontecendo. Cada um desses componentes faz apenas uma coisa, sejam verificações de colisão ou alterações na animação de personagens, não importa, isso depende de cada objeto individual.
Esses objetos retransmitem seu estado de volta e esses dados são passados para
Game.Screen
, o que é essencialmente um manipulador de E / S inverso. Ele mapeia a representação interna em algo que oIO.Screen
componente pode usar para gerar a saída real.fonte
IO
egame
namespaces ou classes com subclasses?O princípio da responsabilidade única pode ser difícil de entender. O que eu achei útil é pensar nisso como você escreve frases. Você não tenta colocar muitas idéias em uma única frase. Cada frase deve indicar claramente uma idéia e adiar os detalhes. Por exemplo, se você quisesse definir um carro, diria:
Em seguida, você definiria coisas como "veículo", "estrada", "rodas" etc. etc. separadamente. Você não tentaria dizer:
Da mesma forma, você deve tentar tornar suas classes, métodos, etc, declarar o conceito central da maneira mais simples possível e adiar os detalhes para outros métodos e classes. Assim como nas frases escritas, não existe uma regra rígida sobre o tamanho delas.
fonte
Eu diria que o melhor caminho a seguir é mantê-los em classes separadas. As turmas pequenas não são ruins, na verdade, na maioria das vezes, são uma boa ideia.
Em relação ao seu caso específico, acho que ter o separado pode ajudá-lo a alterar a lógica de qualquer um desses manipuladores específicos sem afetar os outros e, se necessário, seria mais fácil adicionar um novo método de entrada / saída, se fosse o caso.
fonte
O Diretor de Responsabilidade Única declara que uma classe deve ter apenas um motivo para mudar. Se a sua turma tiver vários motivos para mudar, você poderá dividi-la em outras turmas e utilizar a composição para eliminar esse problema.
Para responder sua pergunta, tenho que fazer uma pergunta: Sua turma tem apenas um motivo para mudar? Caso contrário, não tenha medo de continuar adicionando classes mais especializadas até que cada uma tenha apenas um motivo para mudar.
fonte