Eu criei duas classes abstratas Subject e Observer que definem uma interface padrão clássica do Observer. Derivei deles para implementar o padrão Observer. Um observador pode ficar assim:
void MyClass::Update(Subject *subject)
{
if(subject == myService_)
{
DoSomething();
}
else if(subject == myOtherService_)
{
DoSomethingElse();
}
}
Isso é bom e me diz quem mudou alguma coisa. No entanto, isso não me diz o que mudou. Às vezes, está tudo bem, porque vou consultar o assunto para obter os dados mais recentes, mas outras vezes preciso saber o que exatamente mudou no assunto. Percebo em Java que eles têm um método notifyObservers () e um método notifyObservers (Object arg) para presumivelmente especificar detalhes sobre o que foi alterado.
No meu caso, preciso saber se uma das duas ações diferentes ocorreu sobre o assunto e, se for uma ação específica, saber um número inteiro relacionado a essa ação.
Então, minhas perguntas são:
- qual é a maneira C ++ de passar um argumento genérico (como Java faz)?
- O Observer é o melhor padrão? Talvez algum tipo de sistema de eventos?
ATUALIZAR
Eu encontrei este artigo que fala sobre a modelagem do padrão Observer: Implementando um padrão Subject / Observer com modelos . Isso me fez pensar se você poderia modelar um argumento.
Eu encontrei essa pergunta de estouro de pilha que fala sobre a modelagem do argumento: Padrão de Observer de assunto baseado em modelo - devo usar static_cast ou dynamic_cast . No entanto, o OP parece ter um problema que ninguém respondeu.
A outra coisa que eu poderia fazer é alterar o método Update para obter um objeto EventArg como em:
void MyClass::Update(Subject *subject, EventArg arg)
{
...
E, em seguida, crie subclasses do EventArg para dados de argumentos específicos e, em seguida, suponho que seja convertido de volta para a subclasse específica dentro do método de atualização.
ATUALIZAÇÃO 2
Também foi encontrado um artigo, Ao criar uma estrutura c ++ assíncrona baseada em mensagens; parte 2, que discute como o Assunto comunica detalhes sobre o que mudou.
Agora estou pensando seriamente em usar o Boost.Signals . Usar meu próprio padrão de observador fazia sentido quando era simples, mas modelar o tipo e o argumento está começando a ficar complicado. E talvez eu precise da segurança do thread do Boost.Signals2.
ATUALIZAÇÃO 3
Também encontrei alguns artigos interessantes sobre o padrão de observadores:
Generalizing Observer por Herb Sutter
Implementando o Padrão Observador em C ++ - Parte 1
Experiências de implementação do padrão de design do observador (parte 2)
Experiências de implementação do padrão de design do observador (parte 3)
No entanto, mudei minha implementação para usar o Boost.Signals que, embora possivelmente esteja um pouco inchado para os meus propósitos, está funcionando com êxito. E provavelmente quaisquer preocupações de inchaço ou velocidade são irrelevantes.
fonte
Respostas:
Seja C ++ ou JAVA, uma Notificação para o observador pode vir junto com as informações do que foi alterado. Os mesmos métodos notifyObservers (Object arg) também podem ser usados em C ++.
Geralmente, a questão permanece é que pode haver vários assuntos enviados para um ou vários observadores e, portanto,
class arg
não podem ser codificados.Normalmente, a melhor maneira de fazer é criar arg na forma de mensagens / tokens genéricos que formam o mesmo tipo de dados para várias classes, mas os valores diferem para diferentes classes observadas. Como alternativa, se todos esses valores de notificação forem derivados da classe em alguma classe baseada que é comum a todos.
Para o padrão do observador, é importante que o tipo de dados Arg não seja codificado entre o observador e o observador - caso contrário, é um acoplamento que dificulta a evolução das coisas.
EDITAR
Se você deseja que esse observador não apenas observe, mas também precise executar muitas outras tarefas com base no que foi alterado , verifique também o padrão Visitor . No padrão de visitante, o observador / visitante chama o objeto modificado e, portanto, pode não apenas saber qual é a modificação, mas pode realmente trabalhar nele
fonte
Object
(void *
,boost::any
ou algo semelhante genérico) do que se você passar tipo específico, porque com o tipo específico que você verá em tempo de compilação que algo mudou, enquanto que com o tipo genérico que irá compilar e parar de funcionar, porque o observador não poderá trabalhar com os dados reais passados.std::function
Boostboost::function
, Gtk +GClosure
, métodos vinculados python etc.), então apenas defino métodos com assinaturas apropriadas e solicito ao sistema que crie o real observador. O acoplamento é de fato apenas uma maneira, o assunto define a interface para os observadores, mas não tem idéia sobre sua implementação.Existem algumas maneiras de enviar um argumento de evento genérico "Java Like" em C ++.
1) Declare o argumento do evento como nulo * e faça a conversão para a classe correta no manipulador de eventos.
2) Declare o argumento do evento como um ponteiro / ref para uma nova classe / interface como (em resposta ao seu exemplo)
E as classes de argumentos de eventos reais derivam dessa classe.
Quanto à sua pergunta sobre um observador baseado em modelo, confira
http://www.codeproject.com/Articles/3267/Implementing-a-Subject-Observer-pattern-with-templ
fonte
Não sei se esse é o nome canônico, mas, desde os meus velhos tempos em Smalltalk, lembro-me do termo "aspecto" para identificar o que mudou no observável.
Não é tão complexo quanto a sua idéia de EventArg (e subclassificá-lo); apenas passa uma string (pode até ser uma constante inteira) do observável para o observador.
Além disso: existem apenas dois métodos simples (
update(observable)
eupdateAspect(observable, aspect)
Menos: O observador pode ter que pedir mais informações ao observável (ou seja, seu "número inteiro")
fonte