Jestem mocno wierzy w następującej filozofii projektowania:Projekt lepszy interfejs API przekazać struct z jednej klasy do innej klasy
1> Usługi powinny być realizowane możliwie jak najbliżej, gdzie są przechowywane dane.
2> Getter i Setter są źli i powinni być ostrożnie wykorzystywani.
Raczej nie argumentuję powyżej dwóch argumentów i zakładam, że mają krawędzie.
Oto wyzwanie, przed którym stoję obecnie. Mam dwie klasy (tj. AComputer
i A
), gdzie program AComputer udostępnia niektóre usługi dla A i A zawiera wszystkie podstawowe elementy danych.
Fakt: Nie można łączyć AComputer
wewnątrz A
z powodu projektu systemu. Wiedziałem, że złamało to mój punkt 1> gdzie obliczenia powinny pozostać przy danych.
Przekazując dane od A
do AComputer
, musimy przekazać 10 (około) poszczególnych parametrów i dlatego lepiej jest zaprojektować strukturę, aby to zrobić, w przeciwnym razie lista konstruktorów stanie się szalona. Większość danych przechowywanych w AComputer
to kopie bezpośrednie przechowywane w A
. Zdecydowaliśmy się przechowywać te dane wewnątrz AComputer
, ponieważ inne funkcje w AComputer
również potrzebują tych zmiennych.
Oto jest pytanie (Pytam dla najlepszych praktyk rozważa konserwacja api & modyfikacji):
1> Gdzie powinniśmy zdefiniować pass-strukturę PassData
?
2> Czy należy zapewnić program pobierający/ustawiający dla struct PassData
?
Podałem przykładowy kod, jak pokazano poniżej, aby zilustrować moje pytanie w szczegółach. Najlepiej, gdy znajdę prawdziwy open source, który rozwiązał ten sam problem, abym mógł się z niego uczyć.
Jeśli przyjrzeć się prywatnemu PassData m_data;
zdefiniowanemu w klasie AComputer
, robię to celowo. Innymi słowy, jeśli zmienimy podstawową implementację AComputer
, możemy zastąpić PassData m_data;
pojedynczymi zmiennymi lub czymś innym, ale NIE łamiemy interfejsu PassData
. Tak więc w tym projekcie NIE dostarczam programu pobierającego/ustawiającego dla struktury PassData
.
Dziękuję
class AComputer
{
public:
struct PassData
{ // int type just used as an illustration. Real data has different types,
// such as double, data, string, enum, etc.
// Note: they are not exact copies of variables from A but derived from them
int m_v1;
// from m_v1 to m_v10
//...
int m_v10;
};
// it is better to store the passed-in data since other functions also need it.
AComputer(const PassData& pd) : m_data(pd) {}
int GetCombinedValue() const
{ /* This function returns a value based the passed-in struct of pd */ }
private:
PassData m_data;
};
class A
{
private:
int m_i1;
// from m_i1 to m_i10
// ...
int m_i10;
// from m_i11 to m_i20
// ...
int m_i20;
boost::shared_ptr<AComputer> m_pAComputer;
public:
A()
{
AComputer::PassData aData;
// populate aData ...
m_pAComputer = boost::shared_ptr<AComputer>(new AComputer(aData));
}
int GetCombinedValue() const
{
return m_pAComputer->GetCombinedValue();
}
};
Czy wszystkie argumenty są naprawdę wszystkie "int" (lub wszystkie typy)? Jeśli tak, myślę, że po prostu przekazałbym 'std :: vector'. Jeśli chcesz uzyskać dostęp do nich po nazwie, zdefiniowałbym enum w "AComputer", który podaje nazwy dla indeksów dolnych, więc możesz użyć 'argument [m_i1]' zamiast 'argument [0]'. –
Dobra uwaga. Argumenty są pełne różnych typów, takich jak int, double, string, date, itp. Zaktualizuję mój OP, aby wyjaśnić zamieszanie. – q0987
Co ?! Masz dwie filozofie projektowania i z jakiegoś powodu po prostu * masz *, aby złamać numer 1. Teraz pytasz nas, czy to jest dobry pomysł, aby złamać numer 2? Za czym tęskniłem? –