Czy przy projektowaniu klasy należy preferować spójność w zachowaniu zamiast powszechnej praktyki programowania? Aby podać konkretny przykład:
Powszechna konwencja jest następująca: jeśli klasa jest właścicielem obiektu (np. Go utworzył), jest odpowiedzialna za jego wyczyszczenie po zakończeniu. Konkretnym przykładem może być .NET, że jeśli twoja klasa jest właścicielem IDisposableobiektu, powinna go pozbyć pod koniec swojego życia. A jeśli nie jesteś właścicielem, nie dotykaj go.
Teraz, jeśli spojrzymy na StreamWriterklasę w .NET, możemy znaleźć w dokumentacji, że zamyka ona strumień bazowy, gdy jest on zamykany / usuwany. Jest to konieczne w przypadkach, gdy StreamWritertworzenie instancji następuje poprzez przekazanie nazwy pliku, ponieważ program piszący tworzy podstawowy strumień plików i dlatego musi go zamknąć. Można jednak również przekazać strumień zewnętrzny, który pisarz również zamyka.
Denerwowało mnie to mnóstwo razy (tak, wiem, że możesz zrobić nie zamykające się opakowanie, ale nie o to chodzi), ale najwyraźniej Microsoft podjął decyzję, że bardziej konsekwentne jest zamykanie strumienia bez względu na to, skąd pochodzi.
Kiedy spotykam się z takim wzorcem w jednej z moich klas, zwykle tworzę ownsFooBarflagę, która jest ustawiana na fałsz w przypadkach, gdy FooBarjest wstrzykiwana przez konstruktor, i tak naprawdę jest inaczej. W ten sposób odpowiedzialność za jego wyczyszczenie przechodzi na osobę dzwoniącą, gdy przekazuje instancję w sposób jawny.
Teraz zastanawiam się, czy być może spójność powinna być lepsza niż najlepsza praktyka (a może moja najlepsza praktyka nie jest tak dobra)? Jakieś argumenty za / przeciw?
Edytuj dla wyjaśnienia
Przez „spójność” mam na myśli: konsekwentne zachowanie klasy zawsze przejmującej własność (i zamykającą strumień) w porównaniu z „najlepszą praktyką”, aby przejąć własność obiektu tylko wtedy, gdy go utworzyłeś lub wyraźnie przekazałeś własność.
Na przykład, w którym jest przyjemny:
Załóżmy, że masz dwie podane klasy (z biblioteki innej firmy), które akceptują strumień, aby coś z nim zrobić, na przykład tworzenie i przetwarzanie niektórych danych:
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))
{
....
}
}
}
Teraz chcę użyć tego w następujący sposób:
Result ProcessSomething(DataSource source)
{
var processor = new DataProcessor();
...
var ms = new MemoryStream();
source.GetData(ms);
return processor.ProcessData(ms);
}
To się nie powiedzie z wyjątkiem Cannot access a closed streamw procesorze danych. Jest nieco skonstruowany, ale powinien zilustrować tę kwestię. Istnieją różne sposoby rozwiązania tego problemu, ale wydaje mi się, że pracuję nad czymś, czego nie powinienem.