Форум программистов, компьютерный форум, киберфорум
Наши страницы

C++

Войти
Регистрация
Восстановить пароль
 
 
Kastaneda
Jesus loves me
Эксперт С++
4697 / 2901 / 238
Регистрация: 12.12.2009
Сообщений: 7,385
Записей в блоге: 2
Завершенные тесты: 1
#1

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

17.09.2016, 07:15. Просмотров 1057. Ответов 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
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Немного философии ООП (C++):

Немного про std::string - C++
Привет, читал про std::string на разных сайтах. 1. Там говорят, С++ 11 гарантирует, что std::string будет stored contiguously in...

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

Немного бесплатного от Embarcadero - C++ Builder
Хочу напомнить, может кто не знает, но сегодня последний день, когда можно получить C++ Builder 10.1 Berlin Starter бесплатно. Сам...

Немного глупый вопрос - C++ Builder
Добрый день! У меня есть на форме компоненты Memo1 и Memo2 типа TMemo. Так вот в Memo1 есть большой текст. Короче как мне взять этот весь...

немного теории UPD - C++ Builder
Добрый день! Не мог бы ни кто объяснить про UPD. Почему у клиентов TIdUDPClient, нет как токовых событий Read как от сервера передавать...

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

24
Убежденный
Ушел с форума
Эксперт С++
15708 / 7218 / 1139
Регистрация: 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
2440 / 1133 / 240
Регистрация: 30.11.2013
Сообщений: 3,690
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
Игогошка!
1776 / 678 / 42
Регистрация: 19.08.2012
Сообщений: 1,295
Завершенные тесты: 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
2440 / 1133 / 240
Регистрация: 30.11.2013
Сообщений: 3,690
17.09.2016, 14:14 #5
ct0r, я испол cereal потому, что не испол boost вообще)
0
Kastaneda
Jesus loves me
Эксперт С++
4697 / 2901 / 238
Регистрация: 12.12.2009
Сообщений: 7,385
Записей в блоге: 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
Эксперт С++
4916 / 3024 / 370
Регистрация: 10.11.2010
Сообщений: 11,081
Записей в блоге: 10
Завершенные тесты: 1
18.09.2016, 12:40 #7
Kastaneda, а если включить serialize/deserialize в сам структуру Settings - это плохой подход?
1
Кудаив
329 / 406 / 24
Регистрация: 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
Комп_Оратор)
Эксперт по математике/физике
6511 / 3148 / 308
Регистрация: 04.12.2011
Сообщений: 8,709
Записей в блоге: 5
18.09.2016, 15:11 #9
Цитата Сообщение от Kastaneda Посмотреть сообщение
Семантически структура (набор настроек) - это объект, не обладающий поведением. Добавление метода награждает его поведением, что идейно не правильно.
Разве статический метод отражает поведение? Он же не член экземпляра, чье же поведение он отражает? В целом, для сокрытия имени подойдёт и нэймспейс, конечно, но более упорядоченно это выглядит в классе. Это личное мнение, - я не гуру.
И попутно вопрос. Допустим Вы решили не генерировать исключений в конструкторе, а закрыли его и имеете доступ в статическом методе производящем объекты который проверяет правильность данных перед вызовом конструктора.
Тогда свободная функция-сериализатор тоже должна иметь доступ? Объявить другом? Мне кажется, что брешь в инкапсуляции всё-таки есть. Или нет?
0
DrOffset
7377 / 4454 / 1009
Регистрация: 30.01.2014
Сообщений: 7,304
18.09.2016, 15:43 #10
Был уже похожий "холивар" здесь: Как обратится к обьекту класса, являющегося наследником абстрактного класса
0
Kastaneda
Jesus loves me
Эксперт С++
4697 / 2901 / 238
Регистрация: 12.12.2009
Сообщений: 7,385
Записей в блоге: 2
Завершенные тесты: 1
18.09.2016, 16:59  [ТС] #11
Цитата Сообщение от castaway Посмотреть сообщение
Kastaneda, а если включить serialize/deserialize в сам структуру Settings - это плохой подход?
А зачем структуре знать как себя сериализовывать? Settings - это набор полей, он вообще ничего не должен уметь делать.

Добавлено через 1 минуту
Цитата Сообщение от IGPIGP Посмотреть сообщение
Тогда свободная функция-сериализатор тоже должна иметь доступ? Объявить другом?
Так структура же
0
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
6511 / 3148 / 308
Регистрация: 04.12.2011
Сообщений: 8,709
Записей в блоге: 5
18.09.2016, 18:20 #12
Цитата Сообщение от Kastaneda Посмотреть сообщение
Так структура же
Так создатель же уравнял структуры и классы перед небом... Я понимаю так. Если сериализация зависит от внешних факторов то она может обеспечиваться статическими методами. А если она может зависеть от экземпляра, то должна проводиться методами объектов. Например, (надуманная и не здоровая фантазия) если объект содержит большой объём строковых (символьных) данных, то может обеспечить их сжатие. Вначале, в несжатой части пишутся данные о методе кодирования.
Но, да, это холивар.
Единственно, против чего восстаёт душа это такие слова как "правильно".
Цитата Сообщение от Kastaneda Посмотреть сообщение
, к правильному решению пришли только на следующий день
то есть фломастеры бывают неровными?
0
castaway
Эксперт С++
4916 / 3024 / 370
Регистрация: 10.11.2010
Сообщений: 11,081
Записей в блоге: 10
Завершенные тесты: 1
18.09.2016, 18:23 #13
Цитата Сообщение от Kastaneda Посмотреть сообщение
А зачем структуре знать как себя сериализовывать?
Приведу аналогию. Я - человек, и я лучше других знаю как управлять своим телом.
2
Kastaneda
Jesus loves me
Эксперт С++
4697 / 2901 / 238
Регистрация: 12.12.2009
Сообщений: 7,385
Записей в блоге: 2
Завершенные тесты: 1
18.09.2016, 18:58  [ТС] #14
Цитата Сообщение от IGPIGP Посмотреть сообщение
Так создатель же уравнял структуры и классы перед небом...
Да, но семантически это все же разные вещи. Структуры идейно используются там, где надо просто иметь набор данных без поведения. Класс уже более сложная штука (опять же семантически).

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

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

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

Добавлено через 57 секунд
Цитата Сообщение от Kastaneda Посмотреть сообщение
вроде очевидно?
Да вроде нет.
1
18.09.2016, 19:00
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
18.09.2016, 19:00
Привет! Вот еще темы с ответами:

Помогите немного переделать программу - C++ Builder
Вот код поисковой программы void Scan(AnsiString Path) { TSearchRec sr; if(FindFirst(Path + &quot;*.*&quot;, faAnyFile, sr) == 0) ...

Полезные Хедеры и немного инфы о них - C++ Builder
Сразу к делу... Я сегодня в одном исходнике обнаружил в куче файлов обычно присущих проэкту, хедер файл который я скопировал в другой...

Загрузка изображений через Indy + немного оптимизации - C++ Builder
Я разобрался как используя Indy авторизовываться на файлообменниках :) Теперь мне интересно как можно используя инди залить изображение на...

Немного теории ООП - ООП и паттерны
Добрый день! ООП отражает поведение объектов реального мира. Объект - набор данных, и методов для работы с ними. Например,...


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

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

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