Como usar el patron Strategy c++

Maaarc

Buenas, llevo bastantes días encallado en una practica porque no consigo cambiar el tipo de objeto.

Lo que pasa es que yo tengo un objeto de Clase A (Notification) y de aqui heredan dos clases más que son MailNotification y WAppNotification. Entonces lo que yo quiero hacer es que la Clase User tiene un atributo "Notification * _notification" que de serie es un objeto de tipo MailNotification pero si quiero cambiarlo a WAppNotification no tengo cojones a hacerlo.

#include "Notification.hxx"
#include "MailNotification.hxx"
#include "WAppNotification.hxx"


class User
{
private:
    Notification * _notification;

public:
    User()
    {
        _notification = new MailNotification();
    }                                   
~User() { delete(_notification); } void notify(std::string mSubject) { //Solo envia las notificaciones bien a Mail _notification->notify(descriptionMailSubject(),mSubject); } void setWAppNotification() { // Borro el _notification -> Ahora era de Type MailNotification() delete(_notification); Notification * n; n = new WAppNotification(); // Aqui quiero asignar a Notification *_notification el Type WAppNotification() setNotificationType(n); } void setNotificationType (Notification * mNotification) { _notification = mNotification; }

El objetivo seria que se comportara así:

1) _notification es de tipo MailNotification
1.1) notify() -> ara el notify de la class MailNotification

2) setWAppNotification() -> ahora _notification es de tipo WAppNotification
2.1) notify() -> ara el notify de la clase WAppNotification

Y a lo largo de la ejeccucion del programa que vaya cambiando segun la necesidad.

Muchas gracias ;)!

cabron

He probado este código http://pastebin.com/vi3fEbZ5 que es igual que el tuyo, con la salvedad que me he inventado la implementación de Notification y las subclases, y me imprime correctamente:

Mail primer mensaje

Whatsapp segundo mensaje

Así que tu error tiene que estar en la clase Notification o las subclases

1 1 respuesta
Maaarc

#2 Voy a investigar un poco pero las clases esas son:

#ifndef Notification_hxx
#define Notification_hxx

#include <iostream>
#include <iostream>
#include <string>
#include <vector>
#include "MailStub.hxx"

class Notification{

public:

Notification() {}

virtual ~Notification() {}

virtual void notify(const std::string& to, const std::string& subject) = 0;
//virtual std::string description()=0;
};

#endif
#ifndef WAppNotification_hxx
#define WAppNotification_hxx

#include <iostream>
#include <iostream>
#include <string>
#include <vector>
#include "MailStub.hxx"
#include "Notification.hxx"

class WAppNotification: public Notification
{

public:

WAppNotification() {}

~WAppNotification()	{}

void notify(std::string& phone,std::string& text)
{		
	WhatsappStub::theInstance().sendWhatsapp( phone,text );
}

std::string description()
{
	return "test";
}
};
#endif

/cry

1 respuesta
rafag

#3 El método notify() de Notification recibe como parámetros dos referencias constantes pero en la implementación de WAppNotification no son constantes por lo que, realmente, no estás implementando el método abstracto de Notification sino uno nuevo. Al crear una instancia de esa clase debería saltar un error de compilación. ¿No ocurre eso?

2 1 respuesta
Maaarc

#4 Si xd

void setWAppNotification()
	{	
		delete(_notification); // <----------
		Notification * n;
		n = new MailNotification();
		//WAppNotification n = new WAppNotification();

	setNotificationType(n); // <-------
	delete(n);
	
}

Justo ese metodo es el que me falla saltandome un error de Valgrind "Aborted (core dumped)"

Esas dos lineas son las que me provocan el error, cuando "toqueteo" el Notification * _notification;

p0stm4n

Ya que ha salido esto, he querido recordar una forma de hacer strategy usando tiempo de compilación en lugar de tiempo de ejecución, lo que Alexandrescu llama en su libro Policy-based design.

https://gist.github.com/Facon/5c4eafd737332654672f03de57190104

Lo dejo como mera curiosidad, esta técnica es parecida a lo que tienen implementado por debajo las colecciones de la std (allocators).

1
Maaarc

Ya lo he conseguido.

Muchas gracias a todos!!! Al final lo que he hecho es esto:

Atributos de user:
Notification * _notification;
MailNotification _mailNotification;
WAppNotification _wappNotification;
SMSNotification _smsNotification;

y para cambiar el tipo pues:
_notification = &_laquequiera;
rafag

No me termina de convencer cómo resuelves el problema. Aunque para gustos...

Lo que no me gusta es que hayas puesto los objetos Notification (Mail, Whatsapp, SMS) dentro de la clase User. Al fin y al cabo, la gracia de este patrón es que User no tenga que preocuparse por los distintos tipos de notificaciones y, sin embargo, ahora lo está haciendo.

¿Qué ocurre si en el futuro quieres añadir algo como FacebookNotification?

  1. Tendrías que añadir FacebookNotification como miembro de la clase User.
  2. Tendrías que añadir un método similar a setWAppNotification para hacer el intercambio.
  3. Desde algún otro punto de la aplicación tendrías que llamar a ese método para cambiar el comportamiento.

Por otra parte, esos objetos están siendo construidos en el momento en el que se crea el User y, quizá, nunca se usen. ¿Para qué crearlos?

¿Cómo lo haría yo?

Además de eliminar el atributo [loquesea]Notification tampoco crearía el método set[loquesea]Notification. Me quedaría sólo con el Notification * _notification y el método setNotificationType(...).

Al final quedaría así:

class User
{
private:
    Notification * _notification;

public:
    User()
    {
        //Por defecto no hay ningún tipo.
        _notification = nullptr;
    }                                   
~User() { delete _notification; } void notify(std::string mSubject) { _notification->notify(descriptionMailSubject(),mSubject); } void setNotificationType (Notification * mNotification) { //Se borra el anterior. delete _notification; //Se guarda el nuevo. _notification = mNotification; } }; int main() { User user; user.setNotificationType(new MailNofication()); user.notify("Hola mundo"); user.setNotificationType(new WAppNotification()); user.notify("Hola"); }

(No he comprobado que funcione)

Así, no tendrías que tocar la clase User. Sólo llamar a setNotificationType cuando quieras cambiar el comportamiento y listo.

1 respuesta
Maaarc

#8 No lo he probado tampoco lo que dices aunque me cuadra más con el comportamiento que debería tener un Strategy pero me tengo que adaptar a un TEST_CASE que me dan para testear el codigo haciendo RED-GREEN-REFACTOR y tengo que tener si o si esos metodos.

business.userPrefersSms( "Cellphone user", "123456789" );
business.userPrefersWhatsapp( "Whatsapp user", "987654321" );

Lo que tendre que hacer entre otras cosas es: 
- Buscar el usuario.
- Asignarle el numero de telefono
- Asignar el notify con el Type que toque

Y esa parte una vez tengo esto es bastante facil de hacer y ya lo tengo hecho pero me habia quemado con el Strategy xDD y aunq no este bien del todo almenos funciona. Ya le metere refactor sino que tengo 1 semana y media de margen.

Usuarios habituales

  • Maaarc
  • rafag
  • p0stm4n
  • cabron