Jeśli chcę zapisać i pobrać obiekt, czy powinienem utworzyć inną klasę do obsługi go, czy lepiej byłoby to zrobić w samej klasie? A może mieszanie obu?
Które jest zalecane zgodnie z paradygmatem OOD?
Na przykład
Class Student
{
public string Name {set; get;}
....
public bool Save()
{
SqlConnection con = ...
// Save the class in the db
}
public bool Retrieve()
{
// search the db for the student and fill the attributes
}
public List<Student> RetrieveAllStudents()
{
// this is such a method I have most problem with it
// that an object returns an array of objects of its own class!
}
}
Przeciw. (Wiem, że zalecane są następujące, jednak wydaje mi się to trochę sprzeczne ze spójnością Student
klasy)
Class Student { /* */ }
Class DB {
public bool AddStudent(Student s)
{
}
public Student RetrieveStudent(Criteria)
{
}
public List<Student> RetrieveAllStudents()
{
}
}
Co powiesz na ich mieszanie?
Class Student
{
public string Name {set; get;}
....
public bool Save()
{
/// do some business logic!
db.AddStudent(this);
}
public bool Retrieve()
{
// build the criteria
db.RetrieveStudent(criteria);
// fill the attributes
}
}
design
object-oriented
Ahmad
źródło
źródło
Odpowiedzi:
Zasada jednolitej odpowiedzialności , rozdzielenie obaw i spójność funkcjonalna . Jeśli przeczytasz te pojęcia, otrzymasz odpowiedź: Rozdziel je .
Prostym powodem, dla którego należy oddzielić
Student
klasę „DB” (lubStudentRepository
, aby przestrzegać bardziej popularnych konwencji), jest umożliwienie zmiany „reguł biznesowych” obecnych wStudent
klasie, bez wpływu na kod odpowiedzialny za trwałość i vice- versa.Ten rodzaj separacji jest bardzo ważny, nie tylko między regułami biznesowymi a trwałością, ale także między wieloma problemami w systemie, aby umożliwić wprowadzanie zmian przy minimalnym wpływie na niezwiązane moduły (minimalne, ponieważ czasami jest to nieuniknione). Pomaga budować bardziej niezawodne systemy, które są łatwiejsze w utrzymaniu i bardziej niezawodne w przypadku ciągłych zmian.
Łącząc reguły biznesowe i uporczywość razem, albo jedną klasę jak w twoim pierwszym przykładzie, lub
DB
zależnie od tegoStudent
, łączysz dwie bardzo różne kwestie. Może się wydawać, że należą do siebie; wydają się być spójne, ponieważ używają tych samych danych. Ale o to chodzi: spójność nie może być mierzona wyłącznie danymi dzielonymi między procedurami, należy również wziąć pod uwagę poziom abstrakcji, na którym one istnieją. W rzeczywistości idealny rodzaj spójności jest opisany jako:I najwyraźniej, przeprowadzanie walidacji przez pewien
Student
czas, a także utrwalanie go, nie tworzy „pojedynczego, dobrze zdefiniowanego zadania”. I znowu zasady biznesowe i mechanizmy trwałości to dwa bardzo różne aspekty systemu, które według wielu zasad dobrego projektowania obiektowego powinny być oddzielone.Polecam przeczytać o czystej architekturze , obejrzeć ten wykład o zasadzie pojedynczej odpowiedzialności (w przypadku bardzo podobnego przykładu), a także obejrzeć ten wykład o czystej architekturze . Koncepcje te nakreślają przyczyny takich podziałów.
źródło
Student
klasa powinna być odpowiednio zamknięta, tak? Jak zatem klasa zewnętrzna może zarządzać trwałością stanu prywatnego? Hermetyzacja musi być zrównoważona w stosunku do SoC i SRP, ale po prostu wybór jednego z nich bez dokładnego rozważenia kompromisów jest prawdopodobnie niewłaściwy. Możliwym rozwiązaniem tej zagadki jest użycie prywatnych pakietów w celu użycia kodu trwałości.Oba podejścia naruszają zasadę jednolitej odpowiedzialności. Twoja pierwsza wersja daje
Student
klasie wiele obowiązków i łączy ją z konkretną technologią dostępu do bazy danych. Drugi prowadzi do ogromnejDB
klasy, która będzie odpowiedzialna nie tylko za studentów, ale za wszelkie inne obiekty danych w twoim programie. EDYCJA: twoje trzecie podejście jest najgorsze, ponieważ tworzy cykliczną zależność między klasą DB aStudent
klasą.Więc jeśli nie zamierzasz pisać programu zabawkowego, nie używaj żadnego z nich. Zamiast tego użyj innej klasy, np.
StudentRepository
Do zapewnienia interfejsu API do ładowania i zapisywania, pod warunkiem, że sam zaimplementujesz kod CRUD. Możesz również rozważyć użycie struktury ORM , która może wykonać dla ciebie ciężką pracę (a struktura zazwyczaj będzie egzekwować niektóre decyzje, w których należy umieścić operacje ładowania i zapisywania).źródło
Istnieje wiele wzorców, które można wykorzystać do utrwalania danych. Istnieje wzorzec Jednostki Pracy, wzorzec Repozytorium , jest kilka dodatkowych wzorców, których można używać, takich jak Fasada zdalna i tak dalej.
Większość z nich ma swoich fanów i krytyków. Często chodzi o wybranie tego, co wydaje się najlepiej pasować do aplikacji i trzymanie się go (ze wszystkimi jego zaletami i wadami, tj. Nie używanie obu wzorów jednocześnie ... chyba że naprawdę jesteś tego pewien).
Na marginesie: w twoim przykładzie RetrieveStudent, AddStudent powinny być metodami statycznymi (ponieważ nie są zależne od instancji).
Innym sposobem posiadania klasowych metod zapisu / ładowania jest:
Osobiście zastosowałbym takie podejście tylko w dość małych aplikacjach, być może w narzędziach do użytku osobistego lub gdzie mogę rzetelnie przewidzieć, że nie będę miał bardziej skomplikowanych przypadków użycia niż tylko zapisywanie lub ładowanie obiektów.
Również osobiście zobacz wzór jednostki pracy. Kiedy się go pozna, jest naprawdę dobry zarówno w małych, jak i dużych przypadkach. Jest obsługiwany przez wiele frameworków / apis, na przykład EntityFramework lub RavenDB.
źródło
Jeśli jest to bardzo prosta aplikacja, w której obiekt jest mniej więcej powiązany z magazynem danych i odwrotnie (tzn. Może być uważany za właściwość magazynu danych), wówczas zastosowanie metody .save () dla tej klasy może mieć sens.
Ale myślę, że byłoby to dość wyjątkowe.
Raczej zwykle lepiej pozwolić klasie zarządzać swoimi danymi i funkcjonalnością (jak dobry obywatel OO) i przekazać mechanizm trwałości innej klasie lub zestawowi klas.
Inną opcją jest użycie frameworka trwałości, który deklaruje trwałość deklaracyjnie (jak w przypadku adnotacji), ale nadal eksternalizuje trwałość.
źródło
Jeśli chodzi o
RetrieveAllStudents()
metodę, twoje poczucie jest słuszne, w rzeczywistości jest prawdopodobnie niewłaściwe, ponieważ możesz mieć wiele różnych list studentów. Dlaczego po prostu nie trzymać list (y) pozaStudent
klasą?źródło