c# - method - Clase con método único: ¿mejor enfoque?
static property c# (14)
Digamos que tengo una clase destinada a realizar una sola función. Después de realizar la función, puede destruirse. ¿Hay alguna razón para preferir uno de estos enfoques?
// Initialize arguments in constructor
MyClass myObject = new MyClass(arg1, arg2, arg3);
myObject.myMethod();
// Pass arguments to method
MyClass myObject = new MyClass();
myObject.myMethod(arg1, arg2, arg3);
// Pass arguments to static method
MyClass.myMethod(arg1, arg2, arg3);
Estaba siendo intencionalmente vago sobre los detalles, para tratar de obtener pautas para diferentes situaciones. Pero realmente no tenía en mente funciones simples de biblioteca como Math.random (). Estoy pensando más en las clases que realizan una tarea específica y compleja, pero solo requieren un método (público) para hacerlo.
¿Puede su clase estar estática?
Si es así, me gustaría hacer una clase de ''Utilidades'' en la que pondría todas mis clases de una función.
Creo que si las propiedades de su clase o la instancia de clase no se usarán en constructores o en sus métodos, no se sugiere que los métodos se diseñen como patrones "estáticos". El método estático siempre debe pensarse de manera "de ayuda".
Es posible que puedas evitar la situación todos juntos. Intente refactorizar para obtener arg1.myMethod1(arg2, arg3)
. Cambie arg1 con arg2 o arg3 si tiene más sentido.
Si no tiene control sobre la clase de arg1, entonces decórela:
class Arg1Decorator
private final T1 arg1;
public Arg1Decorator(T1 arg1) {
this.arg1 = arg1;
}
public T myMethod(T2 arg2, T3 arg3) {
...
}
}
arg1d = new Arg1Decorator(arg1)
arg1d.myMethod(arg2, arg3)
El razonamiento es que, en OOP, los datos y los métodos que procesan esos datos pertenecen juntos. Además, obtienes todas las ventajas que Mark mencionó.
Para aplicaciones simples y ayudantes internal
, usaría un método estático. Para aplicaciones con componentes, me encanta el Framework de Extensibilidad Administrada . Aquí hay un extracto de un documento que estoy escribiendo para describir los patrones que encontrarás en mis API.
- Servicios
- Definido por una interfaz de
I[ServiceName]Service
. - Exportado e importado por el tipo de interfaz.
- La implementación única es proporcionada por la aplicación host y se consume internamente y / o por extensiones.
- Los métodos en la interfaz de servicio son seguros para subprocesos.
- Definido por una interfaz de
Como un ejemplo artificial:
public interface ISettingsService
{
string ReadSetting(string name);
void WriteSetting(string name, string value);
}
[Export]
public class ObjectRequiringSettings
{
[Import]
private ISettingsService SettingsService
{
get;
set;
}
private void Foo()
{
if (SettingsService.ReadSetting("PerformFooAction") == bool.TrueString)
{
// whatever
}
}
}
Prefiero la forma estática. Como la Clase no representa un objeto, no tiene sentido crear una instancia de este.
Las clases que solo existen para sus métodos deben dejarse estáticas.
Realmente no sé cuál es la situación aquí, pero me gustaría ponerlo como método en una de las clases a las que pertenecen arg1, arg2 o arg3 - Si puedes decir semánticamente que una de esas clases sería propietaria de la método.
Si este método es sin estado y no necesita pasarlo, entonces tiene más sentido definirlo como estático. Si necesita pasar el método, podría considerar usar un delegate lugar de uno de sus otros enfoques propuestos.
Si no hay ninguna razón para tener una instancia de la clase creada para ejecutar la función, entonces use la implementación estática. ¿Por qué hacer que los consumidores de esta clase creen una instancia cuando no se necesita uno?
Si no necesita guardar el estado del objeto, entonces no hay necesidad de crear una instancia en primer lugar. Me gustaría ir con el único método estático al que le pasas los parámetros.
También advertiría contra una clase de Utils gigante que tiene docenas de métodos estáticos no relacionados. Esto puede ser desorganizado y difícil de manejar a toda prisa. Es mejor tener muchas clases, cada una con pocos métodos relacionados.
Simplemente haría todo en el constructor. al igual que:
new MyClass(arg1, arg2, arg3);// the constructor does everything.
o
MyClass my_object(arg1, arg2, arg3);
Solía amar las clases de utilidad llenas de métodos estáticos. Hicieron una gran consolidación de los métodos de ayuda que, de otro modo, estarían alrededor causando redundancia y mantenimiento. Son muy fáciles de usar, sin creación de instancias, sin eliminación, solo olvídate. Supongo que este fue mi primer intento involuntario de crear una arquitectura orientada a servicios: muchos servicios apátridas que solo hicieron su trabajo y nada más. Sin embargo, a medida que el sistema crece, vienen los dragones.
Polimorfismo
Digamos que tenemos el método UtilityClass.SomeMethod que alegremente suena. De repente, necesitamos cambiar la funcionalidad un poco. La mayor parte de la funcionalidad es la misma, pero tenemos que cambiar un par de partes, no obstante. Si no hubiera sido un método estático, podríamos hacer una clase derivada y cambiar el contenido del método según sea necesario. Como es un método estático, no podemos. Claro, si solo necesitamos agregar funcionalidad antes o después del método anterior, podemos crear una nueva clase y llamar a la anterior, pero eso es simplemente asqueroso.
Problemas de interfaz
Los métodos estáticos no se pueden definir a través de interfaces por razones lógicas. Y dado que no podemos anular los métodos estáticos, las clases estáticas son inútiles cuando necesitamos pasarlas por su interfaz. Esto nos impide usar clases estáticas como parte de un patrón de estrategia. Podríamos corregir algunos problemas pasando delegados en lugar de interfaces .
Pruebas
Esto básicamente va de la mano con los problemas de interfaz mencionados anteriormente. Como nuestra capacidad de intercambiar implementaciones es muy limitada, también tendremos problemas para reemplazar el código de producción con el código de prueba. De nuevo, podemos envolverlos, pero requerirá que cambiemos partes grandes de nuestro código solo para poder aceptar envoltorios en lugar de los objetos reales.
Fosters blobs
Como los métodos estáticos se usan generalmente como métodos de utilidad y los métodos de utilidad generalmente tienen diferentes propósitos, rápidamente terminaremos con una gran clase llena de funcionalidades no coherentes; idealmente, cada clase debe tener un único propósito dentro del sistema. Prefiero tener cinco veces más clases siempre que sus propósitos estén bien definidos.
Parámetro creep
Para empezar, ese pequeño y lindo método estático e inocente podría tomar un solo parámetro. A medida que crece la funcionalidad, se agregan un par de nuevos parámetros. Pronto se agregan más parámetros que son opcionales, por lo que creamos sobrecargas del método (o simplemente agregamos valores predeterminados, en los idiomas que los admiten). En poco tiempo, tenemos un método que toma 10 parámetros. Solo los primeros tres son realmente necesarios, los parámetros 4-7 son opcionales. Pero si se especifica el parámetro 6, también se requieren rellenar 7-9 ... Si hubiéramos creado una clase con el único propósito de hacer lo que este método estático hicimos, podríamos resolver esto tomando en cuenta los parámetros requeridos en el constructor, y permite al usuario establecer valores opcionales a través de propiedades o métodos para establecer múltiples valores interdependientes al mismo tiempo. Además, si un método ha crecido a esta cantidad de complejidad, lo más probable es que tenga que ser de su propia clase.
Exige que los consumidores creen una instancia de clases sin ninguna razón
Uno de los argumentos más comunes es, ¿por qué exigir que los consumidores de nuestra clase creen una instancia para invocar este único método, sin tener uso para la instancia posterior? Crear una instancia de una clase es una operación muy, muy barata en la mayoría de los lenguajes, por lo que la velocidad no es un problema. Agregar una línea adicional de código al consumidor es un costo bajo para sentar las bases de una solución mucho más fácil de mantener en el futuro. Y, por último, si desea evitar crear instancias, simplemente cree un contenedor único de su clase que permita una fácil reutilización, aunque esto exige que su clase sea apátrida. Si no es apátrida, puede crear métodos de envoltura estáticos que manejen todo, al tiempo que le brindan todos los beneficios a largo plazo. Finalmente, también podría crear una clase que oculte la creación de instancias como si fuera un singleton: MyWrapper.Instance es una propiedad que simplemente devuelve MyClass () nueva;
Solo un Sith trata en absolutos
Por supuesto, hay excepciones a mi disgusto por los métodos estáticos. Las verdaderas clases de utilidad que no presentan ningún riesgo para la hinchazón son casos excelentes para los métodos estáticos: System.Convert como ejemplo. Si su proyecto es único y no requiere mantenimiento futuro, la arquitectura general no es muy importante: estática o no estática, realmente no importa, sin embargo, la velocidad de desarrollo sí lo es.
Estándares, estándares, estándares!
El uso de métodos de instancia no le impide usar también métodos estáticos, y viceversa. Mientras haya un razonamiento detrás de la diferenciación y esté estandarizado. No hay nada peor que mirar por encima de una capa de negocios con diferentes métodos de implementación.
Sugeriría que es difícil responder en función de la información proporcionada.
Mi intuición es que si solo vas a tener un método, y que vas a descartar la clase inmediatamente, entonces haz una clase estática que tome todos los parámetros.
Por supuesto, es difícil decir exactamente por qué necesita crear una sola clase solo para este método. ¿Es la situación típica de "clase de servicios públicos" como la mayoría asume? ¿O está implementando algún tipo de clase de regla, de la cual podría haber más en el futuro?
Por ejemplo, tenga esa clase sea enchufable. Entonces desearía crear una interfaz para su único método, y luego querría tener todos los parámetros pasados en la interfaz, en lugar de en el constructor, pero no querría que fuera estático.
Una cuestión más importante a considerar es si el sistema se estaría ejecutando en un entorno multiproceso, y si sería seguro para la ejecución de subprocesos tener un método o variables estáticos ...
Debe prestar atención al estado del sistema.
Yo diría que el formato de Método estático sería la mejor opción. Y también haría la clase estática, de esa manera no tendrías que preocuparte de crear accidentalmente una instancia de la clase.