Al diseñar una clase, ¿debería favorecerse la consistencia en el comportamiento sobre la práctica de programación común? Para dar un ejemplo específico:
Una convención común es esta: si una clase posee un objeto (por ejemplo, lo creó), es responsable de limpiarlo una vez que está hecho. Un ejemplo específico sería en .NET que si su clase posee un IDisposable
objeto, debería disponerlo al final de su vida útil. Y si no lo tienes, no lo toques.
Ahora, si miramos la StreamWriter
clase en .NET, podemos encontrar en la documentación que cierra la secuencia subyacente cuando se cierra / elimina. Esto es necesario en los casos en que StreamWriter
se instancia al pasar un nombre de archivo cuando el escritor crea la secuencia de archivos subyacente y, por lo tanto, debe cerrarla. Sin embargo, también se puede pasar una secuencia externa que el escritor también cierra.
Esto me ha molestado un montón de veces (sí, sé que puedes hacer un contenedor sin cierre, pero ese no es el punto), pero aparentemente Microsoft ha tomado la decisión de que es más consistente cerrar siempre la transmisión sin importar de dónde venga.
Cuando me encuentro con este patrón en una de mis clases, generalmente creo un ownsFooBar
indicador que se establece en falso en los casos en que FooBar
se inyecta a través del constructor y en verdadero de lo contrario. De esta forma, la responsabilidad de limpiarlo se transfiere a la persona que llama cuando pasa la instancia explícitamente.
Ahora me pregunto si tal vez la coherencia debería estar a favor de las mejores prácticas (o tal vez mi mejor práctica no es tan buena). ¿Algún argumento a favor o en contra?
Editar para aclaraciones
Con "consistencia" quiero decir: el comportamiento consistente de la clase siempre tomando posesión (y cerrando la transmisión) frente a "mejores prácticas" para tomar posesión de un objeto solo si lo creó o transfirió explícitamente la propiedad.
En cuanto a un ejemplo donde está enoying:
Suponga que tiene dos clases dadas (de una biblioteca de terceros) que aceptan una secuencia para hacer algo con ella, como crear y procesar algunos datos:
public class DataProcessor
{
public Result ProcessData(Stream input)
{
using (var reader = new StreamReader(input))
{
...
}
}
}
public class DataSource
{
public void GetData(Stream output)
{
using (var writer = new StreamWriter(output))
{
....
}
}
}
Ahora quiero usarlo así:
Result ProcessSomething(DataSource source)
{
var processor = new DataProcessor();
...
var ms = new MemoryStream();
source.GetData(ms);
return processor.ProcessData(ms);
}
Esto fallará con una excepción Cannot access a closed stream
en el procesador de datos. Está un poco construido pero debería ilustrar el punto. Hay varias formas de solucionarlo, pero sin embargo siento que trabajo alrededor de algo que no debería tener que hacerlo.
Respuestas:
También utilizo esta técnica, la llamo 'traspaso' y creo que merece el estado de un patrón. Cuando un objeto A acepta un objeto desechable B como parámetro de tiempo de construcción, también acepta un booleano llamado 'traspaso' (que por defecto es falso) y, si es verdadero, la disposición de A cae en cascada para la disposición de B.
No estoy a favor de la proliferación de las decisiones desafortunadas de otras personas, por lo que nunca aceptaría la mala práctica de Microsoft como una convención establecida, ni consideraría el seguimiento ciego de otras personas de las decisiones desafortunadas de Microsoft como "comportamiento consistente" de ninguna manera .
fuente
handOff
patrón, si dicha propiedad se establece en el constructor, pero existe otro método para modificarlo, ese otro método también debería aceptar unahandOff
bandera. SihandOff
se especificó para el elemento actualmente retenido, debe eliminarse, entonces el nuevo elemento debe aceptarse y lahandOff
bandera interna debe actualizarse para reflejar el estado del nuevo elemento. El método no debería necesitar verificar las referencias antiguas y nuevas para la igualdad, ya que si la referencia original fue "entregada", todas las referencias en poder del llamador original deberían ser abandonadas.Dispose
se ignoraran las solicitudes de pinceles del sistema, un método "color.CreateBrush" podría crear un nuevo pincel (que requeriría eliminación) o devolver un pincel del sistema (que ignoraría la eliminación). La forma más sencilla de evitar la reutilización de un objeto si se preocupa por la eliminación, pero permiten la reutilización si no lo hace, es de deshacerse de él.Creo que tienes razón, para ser honesto. Creo que Microsoft se equivocó con la clase StreamWriter, específicamente, por las razones que usted describe.
Sin embargo, desde entonces he visto una gran cantidad de código en el que las personas ni siquiera intentan deshacerse de su propio Stream porque el marco lo hará por ellos, por lo que arreglarlo ahora hará más daño que bien.
fuente
Yo iría con consistencia. Como mencionó a la mayoría de las personas, tiene sentido que si su objeto crea un objeto hijo, lo poseerá y lo destruirá. Si se le da una referencia desde el exterior, en algún momento "el exterior" también tiene el mismo objeto y no debería ser de su propiedad. Si desea transferir la propiedad desde el exterior a su objeto, utilice los métodos Adjuntar / Separar o un constructor que acepte un valor booleano que deje en claro que la propiedad se transfiere.
Después de escribir todo eso para obtener una respuesta completa, creo que la mayoría de los patrones de diseño genéricos no necesariamente caen en la misma categoría que las clases StreamWriter / StreamReader. Siempre he pensado que la razón por la que MS eligió que StreamWriter / Reader se hiciera cargo automáticamente de la propiedad es porque, en este caso específico, tener una propiedad compartida no tiene mucho sentido. No puede hacer que dos objetos diferentes lean / escriban simultáneamente en la misma secuencia. Estoy seguro de que podrías escribir algún tipo de tiempo compartido determinista, pero eso sería un anti-patrón infernal. Entonces, probablemente es por eso que dijeron, ya que no tiene sentido compartir una transmisión, simplemente tomemos el control. Pero no consideraría que este comportamiento se haya convertido en una convención genérica en todos los ámbitos para todas las clases.
Podría considerar que Streams es un patrón consistente en el que el objeto que se está pasando no es compartible desde el punto de vista del diseño y, por lo tanto, sin ningún indicador adicional, el lector / escritor simplemente se hace cargo de su propiedad.
fuente
Ten cuidado. Lo que usted considera "consistencia" podría ser una colección aleatoria de hábitos.
"Coordinación de interfaces de usuario para la coherencia", Boletín SIGCHI 20, (1989), 63-65. Lo sentimos, no hay enlace, es un artículo viejo. "... un taller de dos días de 15 expertos no pudo producir una definición de consistencia". Sí, se trata de "interfaz", pero creo que si piensa en la "consistencia" por un tiempo, encontrará que tampoco puede definirla.
fuente