A tarefa é configurar um pedaço de hardware dentro do dispositivo, de acordo com algumas especificações de entrada. Isso deve ser alcançado da seguinte maneira:
1) Colete as informações de configuração. Isso pode acontecer em diferentes momentos e lugares. Por exemplo, o módulo A e o módulo B podem solicitar (em momentos diferentes) alguns recursos do meu módulo. Esses 'recursos' são, na verdade, qual é a configuração.
2) Depois que ficar claro que não haverá mais solicitações, um comando de inicialização, fornecendo um resumo dos recursos solicitados, precisa ser enviado ao hardware.
3) Somente depois disso, é possível (e deve) a configuração detalhada dos referidos recursos.
4) Além disso, somente após 2), pode (e deve) o roteamento dos recursos selecionados para os chamadores declarados.
Uma causa comum de bugs, mesmo para mim, que escrevi a coisa, está confundindo essa ordem. Quais convenções, designs ou mecanismos de nomenclatura posso empregar para tornar a interface utilizável por alguém que vê o código pela primeira vez?
fonte
discovery
ouhandshake
?Respostas:
É uma reformulação, mas você pode impedir o uso indevido de muitas APIs, mas sem ter disponível nenhum método que não deva ser chamado.
Por exemplo, em vez de
first you init, then you start, then you stop
Seu construtor
init
é um objeto que pode ser iniciado estart
cria uma sessão que pode ser parada.Obviamente, se você tiver uma restrição para uma sessão de cada vez, precisará lidar com o caso em que alguém tenta criar uma com uma já ativa.
Agora aplique essa técnica ao seu próprio caso.
fonte
zlib
ejpeglib
são dois exemplos que seguem esse padrão para inicialização. Ainda assim, muitas documentações são necessárias para ensinar o conceito aos desenvolvedores.Você pode fazer com que o método de inicialização retorne um objeto que seja um parâmetro necessário para a configuração:
Mesmo se você
MySession
for apenas uma estrutura vazia, isso aplicará através da segurança de tipo que nenhumConfigure()
método pode ser chamado antes da inicialização.fonte
module->GetResource()->Configure(nullptr)
?a, b, c, d
, posso começara
e usá-loMySession
para tentar usá-lob
como um objeto já iniciado, enquanto na realidade não é.Com base na resposta da Cashcow - por que você tem que apresentar um novo objeto ao chamador, quando você pode apenas apresentar uma nova interface? Rebrand-Padrão:
Você também pode permitir que ITerminateable implemente IRunnable, se uma sessão puder ser executada várias vezes.
Seu objeto:
Dessa maneira, você só pode chamar os métodos certos, já que você possui apenas a Interface IStartable no início e obterá o método run () acessível apenas quando você chama start (); Do lado de fora, parece um padrão com várias classes e objetos, mas a classe subjacente permanece uma classe, sempre referenciada.
fonte
Existem muitas abordagens válidas para resolver seu problema. Basile Starynkevitch propôs uma abordagem de “burocracia zero” que deixa você com uma interface simples e depende do programador usar adequadamente a interface. Enquanto eu gosto dessa abordagem, apresentarei outra que tem mais engenharia, mas permite ao compilador detectar alguns erros.
Identificar os vários estados o dispositivo pode ser, como
Uninitialised
,Started
,Configured
e assim por diante. A lista deve ser finita.¹Para cada estado, defina
struct
mantendo as informações adicionais necessárias relevantes para esse estado, por exemploDeviceUninitialised
,DeviceStarted
e assim por diante.Empacote todos os tratamentos em um objeto em
DeviceStrategy
que os métodos usem estruturas definidas em 2. como entradas e saídas. Assim, você pode ter umDeviceStarted DeviceStrategy::start (DeviceUninitalised dev)
método (ou qualquer que seja o equivalente de acordo com as convenções do seu projeto).Com essa abordagem, um programa válido deve chamar alguns métodos na sequência imposta pelos protótipos do método.
Os vários estados são objetos não relacionados, isso ocorre por causa do princípio da substituição. Se for útil que essas estruturas compartilhem um ancestral comum, lembre-se de que o padrão de visitante pode ser usado para recuperar o tipo concreto da instância de uma classe abstrata.
Enquanto eu descrevi em 3. uma
DeviceStrategy
classe única , há situações em que você pode querer dividir a funcionalidade que ela fornece em várias classes.Para resumir, os pontos principais do design que descrevi são:
Por causa do princípio da substituição, os objetos que representam estados do dispositivo devem ser distintos e não ter relações de herança especiais.
Empacote tratamentos de dispositivos em objetos iniciais, e não nos objetos que representam os próprios dispositivos, para que cada dispositivo ou estado do dispositivo veja apenas a si mesmo, e a estratégia os veja e expresse possíveis transições entre eles.
Juro que vi uma vez uma descrição de uma implementação de cliente de telnet seguindo estas linhas, mas não consegui encontrá-la novamente. Teria sido uma referência muito útil!
Nota: Para isso, siga sua intuição ou encontre as classes de equivalência de métodos em sua implementação real para a relação “method₁ ~ method₂ iff. é válido usá-los no mesmo objeto ”- supondo que você tenha um grande objeto encapsulando todos os tratamentos no seu dispositivo. Ambos os métodos de listagem de estados fornecem resultados fantásticos.
fonte
Use um padrão de construtor.
Tenha um objeto que possua métodos para todas as operações mencionadas acima. No entanto, ele não realiza essas operações imediatamente. Apenas lembra de cada operação para mais tarde. Como as operações não são executadas imediatamente, a ordem na qual você as passa para o construtor não importa.
Depois de definir todas as operações no construtor, você chama um
execute
método Quando esse método é chamado, ele executa todas as etapas listadas acima na ordem correta com as operações armazenadas acima. Esse método também é um bom lugar para executar algumas verificações de sanidade que abrangem a operação (como tentar configurar um recurso que ainda não foi configurado) antes de gravá-las no hardware. Isso pode evitar que você danifique o hardware com uma configuração sem sentido (caso o seu hardware seja suscetível a isso).fonte
Você só precisa documentar corretamente como a interface é usada e dar um exemplo de tutorial.
Você também pode ter uma variante da biblioteca de depuração que faz algumas verificações em tempo de execução.
Talvez definir e documentar correctamente algumas convenções de nomenclatura (por exemplo
preconfigure*
,startup*
,postconfigure*
,run*
....)BTW, muitas interfaces existentes seguem um padrão semelhante (por exemplo, kits de ferramentas X11).
fonte
Esse é realmente um tipo de erro comum e insidioso, porque os compiladores só podem impor condições de sintaxe, enquanto você precisa que seus programas clientes estejam "gramaticalmente" corretos.
Infelizmente, as convenções de nomenclatura são quase totalmente ineficazes contra esse tipo de erro. Se você realmente deseja incentivar as pessoas a não fazer coisas não gramaticais, deve distribuir um objeto de comando de algum tipo que deve ser inicializado com valores para as pré-condições, para que elas não possam executar as etapas fora de ordem.
fonte
Usando esse padrão, você tem certeza de que qualquer implementador será executado nesta ordem exata. Você pode dar um passo adiante e criar um ExecutorFactory que criará Executors com caminhos de execução personalizados.
fonte
step1(); step2(); step3();
. O objetivo do construtor de etapas é fornecer uma API que exponha algumas etapas e impor a sequência na qual elas são chamadas. Não deve impedir que um programador faça outras coisas entre as etapas.