design-patterns - patterns - factory pattern
Encapsulando un algoritmo en una clase (6)
¿Podría formularse su pregunta de manera más general como, "cómo usamos el diseño orientado a objetos cuando la idea principal del software es simplemente ejecutar un algoritmo?"
En ese caso, creo que un diseño como el que se ofrece es un buen primer paso, pero a menudo estas cosas dependen del problema.
Creo que un buen diseño general es como lo que tienes allí. . .
class InputData {};
class OutputData {};
class TheAlgorithm
{
private:
//functions and common data
public:
TheAlgorithm(InputData);
//other functions
Run();
ReturnOutputData();
};
Luego, permita que interactúe con main () o con su GUI como lo desee.
Me pregunto cómo (un) común es encapsular un algoritmo en una clase? Más concretamente, en lugar de tener una serie de funciones separadas que envían parámetros comunes entre sí:
void f(int common1, int param1, int *out1);
void g(int common1, int common2, int param1, int *out2)
{
f(common1, param1, ..);
}
para encapsular parámetros comunes en una clase y hacer todo el trabajo en el constructor:
struct Algo
{
int common1;
int common2;
Algo(int common1, int common2, int param)
{ // do most of the work }
void f(int param1, int *out1);
void g(int param1, int *out2);
};
Parece muy práctico no tener que enviar parámetros comunes y resultados intermedios a través de argumentos de funciones ... Pero no he visto este "patrón" siendo ampliamente utilizado ... ¿Cuáles son los posibles inconvenientes?
Hay un patrón de diseño que aborda el problema; se llama "Patrón de diseño de estrategia". Aquí puede encontrar buena información aquí .
Lo bueno de "Estrategia" es que te permite definir una familia de algoritmos y luego usarlos de manera intercambiable sin tener que modificar los clientes que usan los algoritmos.
No es una mala estrategia en absoluto. De hecho, si tiene la capacidad en su lenguaje (que lo hace en C ++) para definir algún tipo de superclase abstracta que define una interfaz opaca para la funcionalidad subyacente, puede intercambiar diferentes algoritmos de entrada y salida en tiempo de ejecución (piense en algoritmos de clasificación, por ejemplo). Si el idioma elegido tiene reflejo, incluso puede tener un código que sea infinitamente extensible, lo que permite que se carguen y utilicen algoritmos que ni siquiera existieron cuando se escribió el consumidor de dichos algoritmos. Esto también le permite acoplar libremente sus otras clases funcionales y clases algorítmicas, lo que es útil para refactorizar y para mantener intacta su cordura cuando trabaja en proyectos grandes.
Por supuesto, cada vez que comience a construir una estructura de clase compleja, habrá una arquitectura adicional, y por lo tanto un código, que será necesario construir y mantener. Sin embargo, en mi opinión, los beneficios a largo plazo superan este pequeño inconveniente.
Una última sugerencia: no hagas tu trabajo en el constructor. Los constructores solo deben usarse para inicializar una estructura interna de clases con valores predeterminados razonables. Sí, esto puede incluir llamar a otros métodos para finalizar la inicialización, pero la inicialización no es operación. Los dos deben estar separados, incluso si requiere una llamada más en su código para ejecutar el algoritmo particular que estaba buscando.
Normalmente creo un functor u objeto de función para encapsular mis algoritmos.
Usualmente uso la siguiente plantilla
class MyFunctor {
public:
MyFunctor( /* List of Parameters */ );
bool execute();
private:
/* Local storage for parameters and intermediary data structures */
}
Luego usé mis funtores de esta manera:
bool success = MyFunctor( /*Parameter*/ ).execute();
Si alguna vez necesitas llamar a ambos métodos con los parámetros del constructor, entonces lo haría.
Si nunca necesitara llamar a ambos métodos con los mismos parámetros, entonces no lo haría.
Tal vez un mejor enfoque (a menos que me falta algo) es ecapsualizar el algoritmo en una clase y hacer que se ejecute a través de una llamada a un método. Puede pasar todos los parámetros a propiedades públicas, a través de un constructor, o crear una estructura que ecapsule todos los parámetros que pasan a una clase que contiene el algoritmo. Pero normalmente no es una buena idea ejecutar cosas en el constructor de esa manera. En primer lugar, porque no es intuitivo.
public class MyFooConfigurator
{
public MyFooConfigurator(string Param1, int, Param2) //Etc...
{
//Set all the internal properties here
//Another option would also be to expose public properties that the user could
//set from outside, or you could create a struct that ecapsulates all the
//parameters.
_Param1 = Param1; //etc...
}
Public ConfigureFoo()
{
If(!FooIsConfigured)
return;
Else
//Process algorithm here.
}
}