Чтобы сделать ваш код свободно связанным, вот несколько простых вещей, которые нужно запомнить:
Часть 1:
Технически известный как «Разделение концерна». Каждый класс играет определенную роль, он должен обрабатывать бизнес-логику или логику приложения. Старайтесь держаться подальше от класса, который сочетает в себе обе обязанности. т.е. класс, который управляет данными (в широком смысле), является логикой приложения, а класс, который использует данные, является бизнес-логикой.
Лично я называю это (в моем собственном маленьком мире) как create it or use it
. Класс должен создать объект или использовать объект, который никогда не должен делать оба.
Часть 2:
Как осуществить разделение интересов.
Для начала есть два простых метода:
Примечание: шаблоны проектирования не являются абсолютными.
Они должны быть адаптированы к ситуации, но имеют основную тему, которая похожа на все приложения. Так что не смотрите на примеры ниже и говорите, что я должен строго следовать этому; это всего лишь примеры (и слегка надуманные).
Инъекция зависимости :
Здесь вы передаете объект, который использует класс. Объект, который вы передаете, основан на интерфейсе, чтобы ваш класс знал, что с ним делать, но не должен знать фактическую реализацию.
class Tokenizer
{
public:
Tokenizer(std::istream& s)
: stream(s)
{}
std::string nextToken() { std::string token; stream >> token;return token;}
private:
std::istream& stream;
};
Здесь мы вводим поток в токенизатор. Токенайзер не знает, к какому типу относится поток, пока он реализует интерфейс std :: istream.
Шаблон сервисного локатора :
Шаблон локатора службы представляет собой небольшое изменение в зависимости от внедрения. Вместо того, чтобы давать объект, который он может использовать, вы передаете ему объект, который знает, как найти (создать) объект, который вы хотите использовать.
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;
};
Здесь мы передаем объект приложения объект персистора. Когда вы выполняете действие сохранения / загрузки, оно использует персистор для создания объекта, который действительно знает, как выполнить действие. Примечание: опять же, персистор является интерфейсом, и вы можете предоставить различные реализации в зависимости от ситуации.
Это полезно, когда potentially
каждый раз при создании экземпляра действия требуется уникальный объект.
Лично я считаю, что это особенно полезно при написании юнит-тестов.
Примечание шаблонов:
Шаблоны проектирования - огромный предмет сам по себе. Это ни в коем случае не исключительный список образцов, которые вы можете использовать, чтобы помочь со слабой связью; это просто общая отправная точка.
С опытом вы поймете, что вы уже используете эти шаблоны, просто вы не использовали их формальные имена. Стандартизируя их имена (и заставляя всех учить их), мы обнаруживаем, что обмениваться идеями легче и быстрее.