2017-01-06 5 views
0

Это вопрос теории не практический.Может ли дизайн шаблонов разрушать абстракцию?

Я пытаюсь разработать программу, которая следует за лучшими практиками программного обеспечения. Но у меня возникли проблемы с поиском наилучшего способа сделать определенные вещи, которые позволяют разработать Test Driven Development и хорошую инкапсуляцию и абстракцию, а также минимизировать сцепление.

Я планировал иметь два уровня абстракции, уровень управления базой данных и уровень управления таблицей.

class TableManager() 
{ 
    init() { 
    manager = DatabaseManger() 
    } 

    do_operation(String operation) { 
     connection = manager.get_db_connection() 
     connection.execute(operation) 
    } 
} 

Это казалось хорошей идеей, потому что фактические данные соединения скрыты от менеджера таблиц. Но это также кажется плохим, потому что TableManager тесно связан с DatabaseManager и для тестирования, мне нужно будет иметь отдельное соединение с базой данных для настройки состояния базы данных, например, для удаления базы данных и таблицы, чтобы мы тестировали операции, когда база данных не существуют и тому подобное

Может кто-то весить здесь и оспаривать мои предположения? Я мог бы переоценить важность некоторых факторов или недооценивать другие. Это обычная проблема с дизайном?

+0

Я думаю, что нет никакого другого пути, затем с абстрактной таблицей управления r, который подходит для каждой базы данных. –

+1

Ну, вы можете передать 'manager' в качестве аргумента конструктора в' TableManager' и иметь 'MockDatabaseManager', который вы можете использовать для тестирования. –

+0

Это на самом деле не так далеко. У меня есть две таблицы, каждая из которых имеет свой собственный менеджер таблиц и имеет свой собственный DBManager. Будет ли смысл иметь DBConnector как отдельный объект и передать в качестве аргумента? – Handcre

ответ

3

Рассмотрим прохождение manager в TableManager в качестве параметра конструктора, так что вы можете иметь две различные реализации менеджера в базы данных: бетонную один и макетом один.

// These two classes have the same interface. 
class ConcreteDBManager 
{ 
    auto numberOfTables() 
    { 
     auto connection = connectToRealDB(); 
     return connection.queryAs<int>("COUNT TABLES"); 
    } 
}; 

class MockDBManager 
{ 
    auto numberOfTables() 
    { 
     return 5; 
    } 
}; 

template <typename TDatabaseManager> 
class TableManager 
{ 
    TDatabaseManager _manager; 
    TableManager(const TDatabaseManager& manager) 
     : _manager{manager} 
    { 
    } 

    auto numberOfTables() 
    { 
     return _manager.numberOfTables(); 
    } 
}; 

При таком подходе, вы можете протестировать функциональность TableManager, предоставляя MockDBManager экземпляр, так что вы не должны подключаться к фактическому БД в модульных тестов ...

TEST(TableManager, RetrieveNumberOfTables) 
{ 
    MockDBManager mockDBManager; 
    TableManager<MockDBManager> tableManager(mockDBManager); 
    EXPECT_EQ(5, tableManager.numberOfTables()); 
} 

... но вы можете подключиться к реальной БД в рабочем коде с тем же TableManager интерфейсом:

void realProductionCode() 
{ 
    ConcreteDBManager concreteDBManager; 
    TableManager<ConcreteDBManager> tableManager(concreteDBManager); 
    std::cout << tableManager.numberOfTables(); 
} 
+0

Да, это кажется хорошей идеей. Я собираюсь сделать это. Есть ли причина иметь отдельный код для mock db и producton db? Я планировал иметь возможность передавать в ip-адрес, номер порта и имя базы данных менеджеру db, поэтому не просто указать базу данных localhost на фактический db_manager так же хорошо, как иметь макетную базу данных, реализованную для тестирования? – Handcre

+1

@Handcre: если вы это сделаете, вы все равно будете полагаться на состояние машины «localhost». Модульные тесты должны проверять функциональность определенного класса - если рассматриваемый класс является «TableManager», тогда вы должны проверять его поведение независимо от любого другого компонента, насколько это возможно. Предоставление макета дает вам независимость от любой реальной базы данных. –

+0

@Handcre Существует абсолютно причина иметь код для макета.Это позволяет лучше тестировать таблицу TableManager, потому что вы не полагаетесь на то, как работает ваш DataBaseManager. Кроме того, модульные тесты будут быстрее, потому что макетные реализации обычно значительно упрощают логику (например, возвращают '5' всегда для' numberOfTables'). – AndyG