Aby luźno powiązać kod, należy pamiętać o kilku prostych sprawach:
Część 1:
Technicznie znany jako „Separation of Concern”. Każda klasa ma określoną rolę, powinna obsługiwać logikę biznesową lub logikę aplikacji. Staraj się omijać klasę, która łączy oba obowiązki. tj. klasa zarządzająca (szerokim terminem) danymi jest logiką aplikacji, podczas gdy klasa, która wykorzystuje dane, jest logiką biznesową.
Osobiście nazywam to (w moim własnym małym świecie) jako create it or use it
. Klasa powinna utworzyć obiekt lub użyć obiektu, którego nigdy nie powinna robić jednocześnie.
Część 2:
Jak wdrożyć rozdział dotyczący obaw.
Punktem wyjścia są dwie proste techniki:
Uwaga: Wzory projektowe nie są absolutne.
Mają być dostosowane do sytuacji, ale mają motyw przewodni podobny do wszystkich aplikacji. Więc nie patrz na poniższe przykłady i nie mów, że muszę ściśle to przestrzegać; są to tylko przykłady (i nieco wymyślone).
Wstrzykiwanie zależności :
Tutaj przekazujesz obiekt używany przez klasę. Obiekt przekazywany na podstawie interfejsu, dzięki czemu klasa wie, co z nim zrobić, ale nie musi znać faktycznej implementacji.
class Tokenizer
{
public:
Tokenizer(std::istream& s)
: stream(s)
{}
std::string nextToken() { std::string token; stream >> token;return token;}
private:
std::istream& stream;
};
Tutaj wstrzykujemy strumień do Tokenizera. Tokenizer nie wie, jakiego typu jest strumień, o ile implementuje interfejs std :: istream.
Wzorzec lokalizatora usług :
Wzorzec lokalizatora usług jest niewielką odmianą wstrzykiwania zależności. Zamiast dać obiekt, którego może użyć, przekazujesz mu obiekt, który wie, jak zlokalizować (utworzyć) obiekt, którego chcesz użyć.
class Application
{
public:
Application(Persister& p)
: persistor(p)
{}
void save()
{
std::auto_ptr<SaveDialog> saveDialog = persistor.getSaveDialog();
saveDialog.DoSaveAction();
}
void load()
{
std::auto_ptr<LoadDialog> loadDialog = persistor.getLoadDialog();
loadDialog.DoLoadAction();
}
private:
Persister& persistor;
};
Tutaj przekazujemy obiekt aplikacji jako obiekt persistor. Podczas wykonywania operacji zapisu / wczytywania wykorzystuje on persistor do utworzenia obiektu, który faktycznie wie, jak wykonać tę akcję. Uwaga: Znowu persistor jest interfejsem i można zapewnić różne implementacje w zależności od sytuacji.
Jest to przydatne, gdy za potentially
każdym razem, gdy tworzona jest akcja, wymagany jest unikalny obiekt.
Osobiście uważam, że jest to szczególnie przydatne podczas pisania testów jednostkowych.
Uwaga wzorów:
Wzory projektowe są dla siebie ogromnym tematem. Nie jest to w żadnym wypadku ekskluzywna lista wzorów, których można użyć, aby pomóc w luźnym sprzężeniu; to tylko wspólny punkt wyjścia.
Z doświadczeniem zdasz sobie sprawę, że już używasz tych wzorów, po prostu nie użyłeś ich formalnych nazw. Dzięki ujednoliceniu ich nazw (i zachęceniu wszystkich do ich nauki) stwierdzamy, że przekazywanie pomysłów jest łatwiejsze i szybsze.