Форум программистов, компьютерный форум, киберфорум
Наши страницы
C++
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.67/6: Рейтинг темы: голосов - 6, средняя оценка - 4.67
Kastaneda
Jesus loves me
Эксперт С++
4949 / 3026 / 346
Регистрация: 12.12.2009
Сообщений: 7,627
Записей в блоге: 2
Завершенные тесты: 1
1

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

17.09.2016, 07:15. Просмотров 1139. Ответов 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, хотел интересных вопросов? :)

2
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
17.09.2016, 07:15
Ответы с готовыми решениями:

Немного про std::string
Привет, читал про std::string на разных сайтах. 1. Там говорят, С++ 11...

Основы ООП [С++]
Немного знаю Java, написал программу которая по заданным 2-м сторонам и углом...

Обработка всех исключений TIdHTTP + немного ООП
Приветствую. У меня возникла потребность в обработке всех исключений компонента...

немного теории UPD
Добрый день! Не мог бы ни кто объяснить про UPD. Почему у клиентов...

Немного бесплатного от Embarcadero
Хочу напомнить, может кто не знает, но сегодня последний день, когда можно...

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

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

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

А сейчас это выглядит, как извращения только ради того, чтобы код был ООП.
0
rikimaru2013
C++ Game Dev
2473 / 1141 / 349
Регистрация: 30.11.2013
Сообщений: 3,709
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();
    }
}
0
ct0r
Игогошка!
1789 / 690 / 44
Регистрация: 19.08.2012
Сообщений: 1,343
Завершенные тесты: 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 содрала оттуда большую часть, просто упростив код новым стандартом.
Да и тянуть дополнительную либу в проект не хочется, а велосипедить такое - как из пушки по воробьям в данном случае.
1
rikimaru2013
C++ Game Dev
2473 / 1141 / 349
Регистрация: 30.11.2013
Сообщений: 3,709
17.09.2016, 14:14 5
ct0r, я испол cereal потому, что не испол boost вообще)
0
Kastaneda
Jesus loves me
Эксперт С++
4949 / 3026 / 346
Регистрация: 12.12.2009
Сообщений: 7,627
Записей в блоге: 2
Завершенные тесты: 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 (вот этого я не знал), при этом семантика использования структуры не нарушается и это реально ООП решение. По ссылку Мейерс расжевывает все это дело более подробно и рассказывает про расширяемость такого подхода, так же там описан один косяк такого подхода, который я не буду сюда дублировать.

Тема создана, чтоб поделиться новыми знаниями, надеюсь кому-нибудь будет полезным
0
castaway
Эксперт С++
4934 / 3039 / 455
Регистрация: 10.11.2010
Сообщений: 11,119
Записей в блоге: 10
Завершенные тесты: 1
18.09.2016, 12:40 7
Kastaneda, а если включить serialize/deserialize в сам структуру Settings - это плохой подход?
1
Кудаив
410 / 409 / 72
Регистрация: 27.05.2012
Сообщений: 1,168
Завершенные тесты: 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 )
0
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
7089 / 3392 / 460
Регистрация: 04.12.2011
Сообщений: 9,439
Записей в блоге: 5
18.09.2016, 15:11 9
Цитата Сообщение от Kastaneda Посмотреть сообщение
Семантически структура (набор настроек) - это объект, не обладающий поведением. Добавление метода награждает его поведением, что идейно не правильно.
Разве статический метод отражает поведение? Он же не член экземпляра, чье же поведение он отражает? В целом, для сокрытия имени подойдёт и нэймспейс, конечно, но более упорядоченно это выглядит в классе. Это личное мнение, - я не гуру.
И попутно вопрос. Допустим Вы решили не генерировать исключений в конструкторе, а закрыли его и имеете доступ в статическом методе производящем объекты который проверяет правильность данных перед вызовом конструктора.
Тогда свободная функция-сериализатор тоже должна иметь доступ? Объявить другом? Мне кажется, что брешь в инкапсуляции всё-таки есть. Или нет?
0
DrOffset
9014 / 4861 / 1196
Регистрация: 30.01.2014
Сообщений: 7,942
18.09.2016, 15:43 10
Был уже похожий "холивар" здесь: Как обратится к обьекту класса, являющегося наследником абстрактного класса
0
Kastaneda
Jesus loves me
Эксперт С++
4949 / 3026 / 346
Регистрация: 12.12.2009
Сообщений: 7,627
Записей в блоге: 2
Завершенные тесты: 1
18.09.2016, 16:59  [ТС] 11
Цитата Сообщение от castaway Посмотреть сообщение
Kastaneda, а если включить serialize/deserialize в сам структуру Settings - это плохой подход?
А зачем структуре знать как себя сериализовывать? Settings - это набор полей, он вообще ничего не должен уметь делать.

Добавлено через 1 минуту
Цитата Сообщение от IGPIGP Посмотреть сообщение
Тогда свободная функция-сериализатор тоже должна иметь доступ? Объявить другом?
Так структура же
0
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
7089 / 3392 / 460
Регистрация: 04.12.2011
Сообщений: 9,439
Записей в блоге: 5
18.09.2016, 18:20 12
Цитата Сообщение от Kastaneda Посмотреть сообщение
Так структура же
Так создатель же уравнял структуры и классы перед небом... Я понимаю так. Если сериализация зависит от внешних факторов то она может обеспечиваться статическими методами. А если она может зависеть от экземпляра, то должна проводиться методами объектов. Например, (надуманная и не здоровая фантазия) если объект содержит большой объём строковых (символьных) данных, то может обеспечить их сжатие. Вначале, в несжатой части пишутся данные о методе кодирования.
Но, да, это холивар.
Единственно, против чего восстаёт душа это такие слова как "правильно".
Цитата Сообщение от Kastaneda Посмотреть сообщение
, к правильному решению пришли только на следующий день
то есть фломастеры бывают неровными?
0
castaway
Эксперт С++
4934 / 3039 / 455
Регистрация: 10.11.2010
Сообщений: 11,119
Записей в блоге: 10
Завершенные тесты: 1
18.09.2016, 18:23 13
Цитата Сообщение от Kastaneda Посмотреть сообщение
А зачем структуре знать как себя сериализовывать?
Приведу аналогию. Я - человек, и я лучше других знаю как управлять своим телом.
2
Kastaneda
Jesus loves me
Эксперт С++
4949 / 3026 / 346
Регистрация: 12.12.2009
Сообщений: 7,627
Записей в блоге: 2
Завершенные тесты: 1
18.09.2016, 18:58  [ТС] 14
Цитата Сообщение от IGPIGP Посмотреть сообщение
Так создатель же уравнял структуры и классы перед небом...
Да, но семантически это все же разные вещи. Структуры идейно используются там, где надо просто иметь набор данных без поведения. Класс уже более сложная штука (опять же семантически).

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

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

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

Добавлено через 57 секунд
Цитата Сообщение от Kastaneda Посмотреть сообщение
вроде очевидно?
Да вроде нет.
1
Kastaneda
Jesus loves me
Эксперт С++
4949 / 3026 / 346
Регистрация: 12.12.2009
Сообщений: 7,627
Записей в блоге: 2
Завершенные тесты: 1
18.09.2016, 19:09  [ТС] 16
castaway, мне на чисто интуитивном уровне кажется, что структура (любая) не должна уметь менять свое представление (упаковываться), этим должен заматься кто-то со стороны, кто знает о наличии этой структуры и формата представления (то, во что сериализуется структура). Сама же структура не должна ничего знать о, скажем, классе DataTransferObject, ей эти знания ни к чему, а без этих знаний она не может упаковывать сама себя в DataTransferObject.
0
castaway
Эксперт С++
4934 / 3039 / 455
Регистрация: 10.11.2010
Сообщений: 11,119
Записей в блоге: 10
Завершенные тесты: 1
18.09.2016, 19:17 17
Kastaneda, так какой же вариант лучший?
0
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
7089 / 3392 / 460
Регистрация: 04.12.2011
Сообщений: 9,439
Записей в блоге: 5
18.09.2016, 19:21 18
Цитата Сообщение от Kastaneda Посмотреть сообщение
мне на чисто интуитивном уровне кажется, что структура (любая) не должна уметь менять свое представление (упаковываться), этим должен заматься кто-то со стороны,
Это значит, что кто-то со стороны должен знать о данной структуре. Я не помню точно, но кажется создатель работая в Центре компьютерных исследований фирмы Bell Labs решал задачи распределения вычислений между различными машинами и различными OS на них. Так как легче, научить внешние ресурсы работе со структурой или попросить разрабов поддержать структуру в разных средах? Конечно, это не о данной структуре, хотя из вопроса не видно, что она примитивна. Иначе вопрос о чём?
0
Kastaneda
Jesus loves me
Эксперт С++
4949 / 3026 / 346
Регистрация: 12.12.2009
Сообщений: 7,627
Записей в блоге: 2
Завершенные тесты: 1
18.09.2016, 19:23  [ТС] 19
Цитата Сообщение от castaway Посмотреть сообщение
Kastaneda, так какой же вариант лучший?
Слово "лучший" тут наверное тоже не совсем уместно. Можно рассуждать так (поэтапно):
1. Мы решили, что Settings не должен уметь сам себя сериализовывать/десериализовывать
2. Значит выносим эти функции в отдельный класс
3. Эти функции не меняют состояние объекта класса, значит должны быть статичными
4. Класс, кроме этих двух статичных функций, больше ничего не содержит, а значит семантически это получается неймспейс
5. Делаем из класса неймспейс, получаем 2 сишные функции

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

Добавлено через 56 секунд
Цитата Сообщение от IGPIGP Посмотреть сообщение
Это значит, что кто-то со стороны должен знать о данной структуре.
да, и о выходном формате. Этого "кого-то" можно считать "специально обученным для этого дела" )
0
castaway
Эксперт С++
4934 / 3039 / 455
Регистрация: 10.11.2010
Сообщений: 11,119
Записей в блоге: 10
Завершенные тесты: 1
18.09.2016, 19:35 20
Цитата Сообщение от Kastaneda Посмотреть сообщение
Получаем 2 функции, которые являются интерфейсными (как пишут умные люди) для структуры Settings, при этом семантика Settings не нарушена.
но ведь:
Цитата Сообщение от Kastaneda Посмотреть сообщение
скатываться до С считается мовитоном и не приветствуется.
0
18.09.2016, 19:35
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
18.09.2016, 19:35

Немного глупый вопрос
Добрый день! У меня есть на форме компоненты Memo1 и Memo2 типа TMemo. Так вот...

Дать немного коментария по коду
Здравсвуйте программисты! Дайте пожалуйста коментарий, желательно подробнее,что...

Помогите немного переделать программу
Вот код поисковой программы void Scan(AnsiString Path) { TSearchRec sr; ...


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru