Форум программистов, компьютерный форум CyberForum.ru

C++

Войти
Регистрация
Восстановить пароль
 
 
Kastaneda
Форумчанин
Эксперт С++
4468 / 2830 / 224
Регистрация: 12.12.2009
Сообщений: 7,199
Записей в блоге: 1
Завершенные тесты: 1
#1

Немного философии ООП - C++

17.09.2016, 07:15. Просмотров 942. Ответов 24
Метки нет (Все метки)

На днях на работе возник холивар с коллегами, к правильному решению пришли только на следующий день, почитав Мейерса и Саттера (один из участников холивара уже знал о правильном решении, но сторонников неправильного было большинство).
Рекомендаций от упомянутых гуру пока не говорю, предлагаю поразмышлять)

Итак - есть проект, в проекте используется ООП, скатываться до С считается мовитоном и не приветствуется.
Есть структура сеттингов
C++
1
2
3
4
5
6
struct Settings
{
    std::string param1;
    int param2;
    // etc
};
понадобилось сделать сериализацию/десериализацию этой структуры. По сути нужны 2 функции serialize(Settings)/deserialize(Settings). Но 2 функции - это С, обернуть их в класс и сделать статичными - это вроде как ООП, но с запашком.
C++
1
2
3
4
5
6
7
class Foo
{
public:
    static PackedObject serialize(Settings);
    
    static Settings deserialize(PackedObject);
};
По сути этот класс является неймспейсом и если из него реально сделать неймспейс (и это правильно), то даже остальной код трогать не придется. т.к. синтаксис использования не изменится. Но это сведет нас к тем же 2м сишным функциям, которых мы хотели избежать.

Вопрос - как сделать правильно, чтоб было хорошее ООП?

Не по теме:

rikimaru2013, хотел интересных вопросов?

Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
17.09.2016, 07:15     Немного философии ООП
Посмотрите здесь:

Немного подправить. C++
немного поменять, C++
C++ Исправить немного
немного усовершенствовать... C++
Подправьте немного... C++
Немного теории C++
C++ Немного не получается
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Убежденный
Системный программист
Эксперт С++
15101 / 6796 / 1073
Регистрация: 02.05.2013
Сообщений: 11,108
Завершенные тесты: 1
17.09.2016, 10:04     Немного философии ООП #2
Цитата Сообщение от Kastaneda Посмотреть сообщение
Вопрос - как сделать правильно
В духе ООП, наверное, было бы нагородить огороды с абстрактными классами IPackedObject,
ISerializable, наплодить Serializer-ов разных типов с какой-нибудь двойной диспетчеризацией...

Цитата Сообщение от Kastaneda Посмотреть сообщение
скатываться до С считается мовитоном
В такой постановке задачи это ООП ради ООП.

Я считаю, что здесь наиболее правильным было оставить подход в стиле C - это самое
простое и очевидное решение. Вот когда кроме Settings у нас будет хотя бы 2-3 других типа,
которые тоже нужно сериализовать и десериализовать, когда будут разные способы
сериализации и т.д., вот тогда да, надо будет подключать ООП на полную.

А сейчас это выглядит, как извращения только ради того, чтобы код был ООП.
rikimaru2013
C++ Game Dev
2377 / 1071 / 234
Регистрация: 30.11.2013
Сообщений: 3,555
17.09.2016, 12:49     Немного философии ООП #3
Kastaneda, конечно хочется )))

Мне нравится решение в cereal:
C++
1
2
3
4
5
struct OtherData
{
    std::string _some;
    SERIALIZE( _some );
};


Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <iostream>
#include <string>
#include <fstream>
 
#include "cereal/cereal.hpp"
#include "cereal/archives/binary.hpp"
#include "cereal/archives/portable_binary.hpp"
#include "cereal/types/string.hpp"
#include "cereal/access.hpp"
 
using namespace std;
 
const std::string FILENAME = "out.bin";
 
#define SERIALIZE( ... )                        \
template<class Archive>                         \
void serialize( Archive & ar )                  \
{                                               \
    ar( __VA_ARGS__ );                          \
}
//////////////////////////////////////////////////////////////////////////
struct Settings
{
    std::string param1;
    int param2;
    float param3; // dont neen
 
    SERIALIZE( param1, param2 );
};
struct OtherData
{
    std::string _some;
    SERIALIZE( _some );
};
//////////////////////////////////////////////////////////////////////////
class Saver
{
private:
    Settings _settings;
    OtherData _data;
 
public:
    void debugInit()
    {
        _settings.param1 = "Privet";
        _settings.param2 = 15;
        _settings.param3 = 0.588f;
 
        _data._some = "Done";
    }
    void saveGame()
    {
        std::ofstream os( FILENAME, std::ios::binary );
        cereal::BinaryOutputArchive archive( os );
        archive( CEREAL_NVP( _settings ) );
        archive( CEREAL_NVP( _data ) );
    }
    void loadGame()
    {
        std::ifstream is( FILENAME, std::ios::binary );
        const bool isOpened = is.is_open();
        if ( isOpened )
        {
            cereal::BinaryInputArchive archive( is );
            archive( CEREAL_NVP( _settings ) );
            archive( CEREAL_NVP( _data ) );
        }
    }
    void print()
    {
        cout << "Settings: " << _settings.param1 << "  " << _settings.param2 << "  " << _settings.param3 << "   " << endl;
        cout << "OtherData: " << _data._some << "  " << endl;
    }
};
//////////////////////////////////////////////////////////////////////////
int main()
{
    const bool isSaveBuild = true;
 
    if ( isSaveBuild )
    {
        Saver saver;
        saver.debugInit();
        saver.saveGame();
    }
    else
    {
        Saver saver;
        saver.loadGame();       
        saver.print();
    }
}
ct0r
Игогошка!
1762 / 664 / 42
Регистрация: 19.08.2012
Сообщений: 1,261
Завершенные тесты: 1
17.09.2016, 14:09     Немного философии ООП #4
Kastaneda, если у нас вот такая одна внезапно появилась структура в виде исключения, которую надо заставить уметь сериализацию, то пару "сишных" функций я считаю самым приемлемым вариантом пока. И с ООП тут вообще никаких проблем -
http://www.gotw.ca/publications/mill02.htm
http://www.gotw.ca/publications/mill08.htm

Цитата Сообщение от rikimaru2013 Посмотреть сообщение
Мне нравится решение в cereal:
Это решение не cereal, а boost.serialization.
cereal содрала оттуда большую часть, просто упростив код новым стандартом.
Да и тянуть дополнительную либу в проект не хочется, а велосипедить такое - как из пушки по воробьям в данном случае.
rikimaru2013
C++ Game Dev
2377 / 1071 / 234
Регистрация: 30.11.2013
Сообщений: 3,555
17.09.2016, 14:14     Немного философии ООП #5
ct0r, я испол cereal потому, что не испол boost вообще)
Kastaneda
Форумчанин
Эксперт С++
4468 / 2830 / 224
Регистрация: 12.12.2009
Сообщений: 7,199
Записей в блоге: 1
Завершенные тесты: 1
18.09.2016, 08:42  [ТС]     Немного философии ООП #6
Цитата Сообщение от Убежденный Посмотреть сообщение
В такой постановке задачи это ООП ради ООП.
ага, но хотелось найти ООП'шное решение

Цитата Сообщение от ct0r Посмотреть сообщение
то пару "сишных" функций я считаю самым приемлемым вариантом пока. И с ООП тут вообще никаких проблем
Да, одну из этих ссылок я читал. Плюс хочется поделится вот этой, рекомендую к прочтению, кому ниженаписанная информация покажется новой.

В общем открыл для себя такую вещь - свободные "сишные" функции могут являтся интерфейсом класса (внезапно %)), плюс они улучшают инкапсуляцию и это вполне себе ООП решение. Покажу на простом примере:

Неправильный подход использования структуры

C++
1
2
3
4
5
6
struct Settings
{
    void setDefault(); // пишет дефолтные значения в  поля структуры
 
    // какие-то поля
};
Семантически струкрута (набор настроек) - это объект, не обладающий поведением. Добавление метода награждает его поведением, что идейно не правильно. Правильно делать так

C++
1
2
3
4
5
6
struct Settings
{
    // какие-то поля
};
 
Settings makeDefaultSettings();
Здесь свободная функция makeDefaultSettings является интерфейсом Settings (вот этого я не знал), при этом семантика использования структуры не нарушается и это реально ООП решение. По ссылку Мейерс расжевывает все это дело более подробно и рассказывает про расширяемость такого подхода, так же там описан один косяк такого подхода, который я не буду сюда дублировать.

Тема создана, чтоб поделиться новыми знаниями, надеюсь кому-нибудь будет полезным
castaway
Эксперт С++
4876 / 3015 / 370
Регистрация: 10.11.2010
Сообщений: 11,075
Записей в блоге: 10
Завершенные тесты: 1
18.09.2016, 12:40     Немного философии ООП #7
Kastaneda, а если включить serialize/deserialize в сам структуру Settings - это плохой подход?
Кудаив
329 / 406 / 24
Регистрация: 27.05.2012
Сообщений: 1,165
Завершенные тесты: 2
18.09.2016, 15:04     Немного философии ООП #8
Цитата Сообщение от Kastaneda Посмотреть сообщение
В общем открыл для себя такую вещь - свободные "сишные" функции могут являтся интерфейсом класса (внезапно %)), плюс они улучшают инкапсуляцию и это вполне себе ООП решение.
если я правильно понял изложенное то это, что то в духе

h файл
C++
1
2
void makeBla();
void makeBlaBla(Bla*);
cpp файл
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Action
{
public:
    void bla();
    void bla(Bla*);
};
 
Action act;
 
void makeBla()
{
   act.bla();
}
 
 
void makeBlaBla(Bla* b)
{
   act.blaBla(b);
}
?

чем, активно грешит boost )
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
6411 / 3050 / 300
Регистрация: 04.12.2011
Сообщений: 8,237
Записей в блоге: 3
18.09.2016, 15:11     Немного философии ООП #9
Цитата Сообщение от Kastaneda Посмотреть сообщение
Семантически структура (набор настроек) - это объект, не обладающий поведением. Добавление метода награждает его поведением, что идейно не правильно.
Разве статический метод отражает поведение? Он же не член экземпляра, чье же поведение он отражает? В целом, для сокрытия имени подойдёт и нэймспейс, конечно, но более упорядоченно это выглядит в классе. Это личное мнение, - я не гуру.
И попутно вопрос. Допустим Вы решили не генерировать исключений в конструкторе, а закрыли его и имеете доступ в статическом методе производящем объекты который проверяет правильность данных перед вызовом конструктора.
Тогда свободная функция-сериализатор тоже должна иметь доступ? Объявить другом? Мне кажется, что брешь в инкапсуляции всё-таки есть. Или нет?
DrOffset
6851 / 4062 / 927
Регистрация: 30.01.2014
Сообщений: 6,859
18.09.2016, 15:43     Немного философии ООП #10
Был уже похожий "холивар" здесь: Как обратится к обьекту класса, являющегося наследником абстрактного класса
Kastaneda
Форумчанин
Эксперт С++
4468 / 2830 / 224
Регистрация: 12.12.2009
Сообщений: 7,199
Записей в блоге: 1
Завершенные тесты: 1
18.09.2016, 16:59  [ТС]     Немного философии ООП #11
Цитата Сообщение от castaway Посмотреть сообщение
Kastaneda, а если включить serialize/deserialize в сам структуру Settings - это плохой подход?
А зачем структуре знать как себя сериализовывать? Settings - это набор полей, он вообще ничего не должен уметь делать.

Добавлено через 1 минуту
Цитата Сообщение от IGPIGP Посмотреть сообщение
Тогда свободная функция-сериализатор тоже должна иметь доступ? Объявить другом?
Так структура же
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
6411 / 3050 / 300
Регистрация: 04.12.2011
Сообщений: 8,237
Записей в блоге: 3
18.09.2016, 18:20     Немного философии ООП #12
Цитата Сообщение от Kastaneda Посмотреть сообщение
Так структура же
Так создатель же уравнял структуры и классы перед небом... Я понимаю так. Если сериализация зависит от внешних факторов то она может обеспечиваться статическими методами. А если она может зависеть от экземпляра, то должна проводиться методами объектов. Например, (надуманная и не здоровая фантазия) если объект содержит большой объём строковых (символьных) данных, то может обеспечить их сжатие. Вначале, в несжатой части пишутся данные о методе кодирования.
Но, да, это холивар.
Единственно, против чего восстаёт душа это такие слова как "правильно".
Цитата Сообщение от Kastaneda Посмотреть сообщение
, к правильному решению пришли только на следующий день
то есть фломастеры бывают неровными?
castaway
Эксперт С++
4876 / 3015 / 370
Регистрация: 10.11.2010
Сообщений: 11,075
Записей в блоге: 10
Завершенные тесты: 1
18.09.2016, 18:23     Немного философии ООП #13
Цитата Сообщение от Kastaneda Посмотреть сообщение
А зачем структуре знать как себя сериализовывать?
Приведу аналогию. Я - человек, и я лучше других знаю как управлять своим телом.
Kastaneda
Форумчанин
Эксперт С++
4468 / 2830 / 224
Регистрация: 12.12.2009
Сообщений: 7,199
Записей в блоге: 1
Завершенные тесты: 1
18.09.2016, 18:58  [ТС]     Немного философии ООП #14
Цитата Сообщение от IGPIGP Посмотреть сообщение
Так создатель же уравнял структуры и классы перед небом...
Да, но семантически это все же разные вещи. Структуры идейно используются там, где надо просто иметь набор данных без поведения. Класс уже более сложная штука (опять же семантически).

Цитата Сообщение от IGPIGP Посмотреть сообщение
то есть фломастеры бывают неровными?
Да, слово "правильно" здесь неуместно, согласен)

Цитата Сообщение от castaway Посмотреть сообщение
Приведу аналогию. Я - человек, и я лучше других знаю как управлять своим телом.
Но пихать в структуру Setting метод serialize это ведь совсем перебор, вроде очевидно? Еще аналогия - усли там есть строка - название MP3 файла, то пихать туда же метод playMp3() это ведь глупо?

Добавлено через 1 минуту
Цитата Сообщение от Кудаив Посмотреть сообщение
чем, активно грешит boost )
И половина STL на этом держится)
castaway
Эксперт С++
4876 / 3015 / 370
Регистрация: 10.11.2010
Сообщений: 11,075
Записей в блоге: 10
Завершенные тесты: 1
18.09.2016, 19:00     Немного философии ООП #15
Цитата Сообщение от Kastaneda Посмотреть сообщение
Еще аналогия - усли там есть строка - название MP3 файла, то пихать туда же метод playMp3() это ведь глупо?
Конечно глупо, это ведь Settings, а не Player.

Добавлено через 57 секунд
Цитата Сообщение от Kastaneda Посмотреть сообщение
вроде очевидно?
Да вроде нет.
Kastaneda
Форумчанин
Эксперт С++
4468 / 2830 / 224
Регистрация: 12.12.2009
Сообщений: 7,199
Записей в блоге: 1
Завершенные тесты: 1
18.09.2016, 19:09  [ТС]     Немного философии ООП #16
castaway, мне на чисто интуитивном уровне кажется, что структура (любая) не должна уметь менять свое представление (упаковываться), этим должен заматься кто-то со стороны, кто знает о наличии этой структуры и формата представления (то, во что сериализуется структура). Сама же структура не должна ничего знать о, скажем, классе DataTransferObject, ей эти знания ни к чему, а без этих знаний она не может упаковывать сама себя в DataTransferObject.
castaway
Эксперт С++
4876 / 3015 / 370
Регистрация: 10.11.2010
Сообщений: 11,075
Записей в блоге: 10
Завершенные тесты: 1
18.09.2016, 19:17     Немного философии ООП #17
Kastaneda, так какой же вариант лучший?
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
6411 / 3050 / 300
Регистрация: 04.12.2011
Сообщений: 8,237
Записей в блоге: 3
18.09.2016, 19:21     Немного философии ООП #18
Цитата Сообщение от Kastaneda Посмотреть сообщение
мне на чисто интуитивном уровне кажется, что структура (любая) не должна уметь менять свое представление (упаковываться), этим должен заматься кто-то со стороны,
Это значит, что кто-то со стороны должен знать о данной структуре. Я не помню точно, но кажется создатель работая в Центре компьютерных исследований фирмы Bell Labs решал задачи распределения вычислений между различными машинами и различными OS на них. Так как легче, научить внешние ресурсы работе со структурой или попросить разрабов поддержать структуру в разных средах? Конечно, это не о данной структуре, хотя из вопроса не видно, что она примитивна. Иначе вопрос о чём?
Kastaneda
Форумчанин
Эксперт С++
4468 / 2830 / 224
Регистрация: 12.12.2009
Сообщений: 7,199
Записей в блоге: 1
Завершенные тесты: 1
18.09.2016, 19:23  [ТС]     Немного философии ООП #19
Цитата Сообщение от castaway Посмотреть сообщение
Kastaneda, так какой же вариант лучший?
Слово "лучший" тут наверное тоже не совсем уместно. Можно рассуждать так (поэтапно):
1. Мы решили, что Settings не должен уметь сам себя сериализовывать/десериализовывать
2. Значит выносим эти функции в отдельный класс
3. Эти функции не меняют состояние объекта класса, значит должны быть статичными
4. Класс, кроме этих двух статичных функций, больше ничего не содержит, а значит семантически это получается неймспейс
5. Делаем из класса неймспейс, получаем 2 сишные функции

Получаем 2 функции, которые являются интерфейсными (как пишут умные люди) для структуры Settings, при этом семантика Settings не нарушена.

Добавлено через 56 секунд
Цитата Сообщение от IGPIGP Посмотреть сообщение
Это значит, что кто-то со стороны должен знать о данной структуре.
да, и о выходном формате. Этого "кого-то" можно считать "специально обученным для этого дела" )
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
18.09.2016, 19:35     Немного философии ООП
Еще ссылки по теме:

Обработка всех исключений TIdHTTP + немного ООП C++ Builder
C++ немного об указателях
подправить немного C++
Немного шаблонов C++
Немного о sizeof C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
castaway
Эксперт С++
4876 / 3015 / 370
Регистрация: 10.11.2010
Сообщений: 11,075
Записей в блоге: 10
Завершенные тесты: 1
18.09.2016, 19:35     Немного философии ООП #20
Цитата Сообщение от Kastaneda Посмотреть сообщение
Получаем 2 функции, которые являются интерфейсными (как пишут умные люди) для структуры Settings, при этом семантика Settings не нарушена.
но ведь:
Цитата Сообщение от Kastaneda Посмотреть сообщение
скатываться до С считается мовитоном и не приветствуется.
Yandex
Объявления
18.09.2016, 19:35     Немного философии ООП
Ответ Создать тему
Опции темы

Текущее время: 04:15. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru