Прошу оценить код 4
Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.
Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.
Доброго времени суток всем!!!
С Вашего позволения я продолжу начатую мною и оконченную здесь тему.
Все кто уже мало мальски ознакомился с классами прошу скинуть свой e-mail сюда
я вышлю исходники и хедер (или в формате .txt как будет Вам удобно) написанной мною программы, дабы получить комментарии, советы , в общем, камни в свой огород :)))
Код не выкладываю в блог по причине объема (около 750 строк). Не уверен «прожует» ли редактор форума такой объем :) В прочем если Selevit разрешит я могу разместить код и здесь.
Череп, Selevit, Алан, porshe как всегда интересно Ваше мнение. Кстати Череп, тема по ссылке окончена на нашем с тобою диалоге и твоего пожелания упаковать мою программу в класс. В общем упаковал...
Alf, письмо не отправляется:
Вот мой адрес:
11porshe99@mail.ru
На первый взгляд — «ошибка» — частое использование
system
, что ни есть хорошо. Очистку экрана можно( весьма топорно, но ведь работает? ) реализовать так:Правда этот код вряд ли быстр и переносим, но в
Windows
будет работать, и его быстродействия хватит для рядовых программ.Больше никаких косяков, заметных мне, не вижу.
Если ты выложишь архив с кодом на файлообменник, всем будет удобнее.
Круто! Только оно у меня не компилится:
И еще скажи зачем у тебя файлы программы продублированы с расширением txt?
Я вижу, что твой компилятор ругается на то что в файле
mainPablicServices.cpp
объявлена внешняя переменная классаistream cin
. Прошу прощения не включил в файл объявлениеusing std::cin;
, хотя у porshe программа скомпилировалась, хз ???В общем я все изменил, убрал
.txt
файлы и ссылку на файлообменник поменял.у меня было больше ошибок, я их просто молча исправил :)
всех обманул )))
Я тоже уже сам поправил баги. Компилятор почему то не захотел кушать русскую «с» в названии функции changePricesBenefits(), и не понял приведение типа в виде
if (choice > unsigned int(n))
.Alf, если то что ты выложил это рабочий код, то как он у тебя откомпилировался?
С исходным текстом я еще не разобрался :( Очень много написано.
Мне не понравилось устройство меню. Как то не логично оно устроено. В «расчеты» можно войти без установки «месяца расчета», но в самих расчетах месяц уже установить нельзя — надо выйти в меню на уровень вверх.
Еще мне показалось что программа неправильно считает. Я не разбирался в деталях по исходнику, но мне кажется что параметры «Льгота» и «Лимит по льготе» вообще нигде не учитывается. А по электричеству разве в пределах 150квт не должно считаться по более низкой ставке? То есть например если потратили 200квт, то первые 150квт считать по более низкой ставке, а последующие 50квт — по более высокой?
Еще хорошо бы, что бы в сохраненном отчете сохранялись не только деньги, но и все параметры расчета: показания счетчиков, разность показаний, тариф. И еще, что бы каждый расчет записывался в свой файл, а не затирал предыдущий расчет. Уникальное имя файла можно делать из номера года и номера месяца. Тогда можно будет понять когда, сколько и по каким тарифам заплатили. Думаю что на Украине тарифы на коммуналку тоже растут регулярно :(
Откомпилировал на VS 2010 и пашет как папа Карло :)))
Но суть в другом, если ты все таки собрал программу, то ты уже второй человек у которого она работает, на мой взгляд, с очень БОЛЬШОЙ и жирной хохмой, но об этом чуть позже...
Ошибку с буквой 'с' исправил.
В расчеты можно войти без выбора месяца для того чтобы посмотреть предыдущий расчет. Но твоя логика верна, при выборе расчета нужно дать возможность выбрать месяц. Исправил.
Новая ссылка на файлообменник
http://dropmefiles.com/d5uLF
Все правильно показалось. Я не оговорил, что функция
calculations();
не содержит расчетной части, а только умножает разницу на основной тариф (т.к. не это самое главное на данном этапе) в целях просмотра работоспособности :)А чего за хохма-то?
Выкладывайте код сюда, либо на https://gist.github.com/
OK selevit сюда, так сюда :)
Макар хохма заключается в функции
changePricesAndBenefits()
в ней вызывается функцияsaveData
которая возвращает типbool
без присваивания возвращаемого значения какой либо переменной ??? Объяснить я этого не могу, но программа работает у меня, у тебя и у porshe.Макар если будешь собирать программу, то лучше копируй все файлы от сюда, а не с файлообменника, т.к. здесь файлы отредактированы.
Вот такая хохма :)))
Файл PablicServices.h
Файл mainPablicServices.cpp
Файл PablicServices.cpp
Alf, никакой хохмы в игнорировании значения, возвращаемого функцией, нет. Это обычное дело. Между прочим, функция
printf
тоже возвращает значение типаint
— количество выведенных символов или код ошибки. Однакоprintf("Hello, World!)
работает ;)У тебя хохма начнется, если
changePricesAndBenefits()
почему то не сможет записать записать данные в файл. Тогда будешь мучительно отлаживать, помня о том, что проверка на ошибку записи у тебя вsaveData
есть.Alf, вопрос первый: а ты сам доволен своим кодом? Я понимаю, что для курицы только что снесённое яйцо всегда золотое. Но вот прошло немного времени, уровень эндорфинов приблизился к норме... ну, честно, доволен?
Вопрос второй. На сколько просто было отлаживать эту программу? На сколько просто вносить в неё изменения? А через пол-года?
Когда я первый раз смотрел исходник, у меня был шок. Такое впечатление, что ты нарушил все писанные и неписанные правила ООП. Попробуем разобраться...
Идеологические ошибки
(1) Программа плохо спроектирована. Перед тем, как писать код, надо сесть и очень хорошо подумать. Составить тех. задание (ТЗ). Возможно нарисовать схему основных потоков управления и основных потоков данных. Выделить сущности, которые имеют определённую функциональность, с прицелом реализовать их в виде классов. Определить взаимодействия этих сущностей, т.е. прикинуть набор методов для классов. Ну и так далее... (На эту тему не одна книга написана, так что я с этим абзацем закругляюсь.)
(2) Декомпозиция нужна для разделения большой сложной задачи на более мелкие, более простые и более понимаемые задачи. И ООП — хороший инструмент для этого. Твой класс
PablicServices
не помогает сделать задачу более простой. В данном случае класс — просто обёртка вокруг обычного процедурного кода с использованием глобальных переменных.(3) То, что называется "бизнес-логика" должно быть, как минимум, отделено от интерфейса пользователя. Представь себе, что консольное приложение ты соберёшься переделать в GUI-приложение. Если по-хорошему, то надо будет сделать только GUI: остальное уже сделано и отлажено.
(4) Есть такое правило хорошего стиля программирования: функция должна умещаться на одном экране. Оно, конечно, опциональное и эмпирическое... Но когда я вижу
PablicServices::mainMenu()
— это правило невольно вспоминается.(5) Повсеместное использование «магических чисел».
Что означают эти числа. И почему в одном случае написано
DIMS[n + 3]
, а в другомDIMS[n + 1]
, хотя в обоих случаях имеется ввиду ссылка на одну и ту же строку в массивеDIMS
?(6) Местами очень запутанный код. Например в логическом спагетти
PablicServices::mainMenu()
я так до конца и не разобрался, несмотря на наличие комментариев :(Технические ошибки и недочёты
(1) Заголовочный файл не должен содержать ничего, кроме объявлений. Т.е. компиляция заголовочного файла не должна порождать ни кода, ни данных. Поэтому раздел констант из PablicServices.h надо скопировать в PablicServices.cpp, а в PablicServices.h убрать все инициализаторы и везде добавить спецификатор
extern
.(2) Деструктор в классе
PablicServices
не нужен.(3) Конструктор предназначен для инициализации объекта. У тебя, помимо инициализации переменных, в конструкторе стоит вызов функции
loadPricesAndBenefits(enter)
, которая реализует добрую треть функциональности программы (не объекта). И к тому же в ней есть куча мест, чреватая нештатными ситуациями (файловый ввод-вывод, взаимодействие с пользователем и пр.) Таких вещей лучше избегать.TO DO
(1) Вернуться на этап проектирования.
(1.1) Разделить пользовательский интерфейс и бизнес-логику.
(1.2) Выделить сущности, готовые жить собственной жизнью.
Это может быть, например, тариф на ресурс, включая льготы и пр., что необходимо для расчёта, а также сам расчёт. В этом случае в качестве полезного выхода имеем либо одно число (посчитанные деньги), либо другой объект, типа «результат», содержащий не только деньги, но и показания счётчика, номер месяца и т.п.
Это также может быть и объект, содержащий не только «тариф» (см. выше), но и посчитанный результат. Тогда с одной стороны увеличиваем сложность объекта, а с другой — улучшаем функциональность: в качестве результата объект может выдать пользователю (и в файл журнала!) не только сумму, но и полную информацию по использовавшемуся тарифу (тарифы меняются).
(1.3) Спроектировать пользовательский интерфейс.
(2) По результатам (1) переписать программу, используя имеющиеся наработки.
(3) Оценить дело рук своих. В случае неудовлетворения вернуться к (1).
Программирование — процесс итерационный. В замкнутой системе программист-программа количество итераций определяется только степенью перфекционизма программиста.
На момент написания этого класса, я прочитал первую главу из раздела «Классы» — «Раздельная компиляция» (Стивен Пратта). Естественно руки зачесались и захотелось творить:).
Сначала хотел взять за основу свой последний вариант, но оказалось не все так просто.
Заново разложил все по полочкам нарисовал схему взаимодействия, но когда стал писать, то тут, то там стали вылезать разные косяки. Закралась мысль, которую я сейчас обдумываю, — нужно разбить некоторые функции на более мелкие, создать наверное три класса, которые будут отвечать за интерфейс, расчетную часть и за вывод, чтение и сохранение данных, данные о тарифах и льготах, которые хранятся в одном массиве, разделить (отдельно газ, свет, вода).
В этот момент на форуме была дискуссия в которой упоминалась литература. Начал читать Дейтела (кстати говоря, как по мне, для начинающего программиста, у которого нет вообще никакого понятия о программировании, эта книга произведет эффект нейтронной бомбы:) Я думаю — должна быть хоть какая-то база, а тут сразу классы, объекты ...). Но для меня очень кстати, что книга начинается с нужной мне темы. Прочитал три первых главы (функции setтеры и getтеры) и мое подозрение укрепилось. Теперь зреет другой замысел (аля перфекционизм!).
В общем, ответ такой — кодом не доволен.
Единственное что в нем порадовало — это девяностопятипроцентная защита от «дурака» (защиту от ввода EOF я не вставлял, что в принципе легко исправить).
В принципе в отладке проблем не было, но через пол года не знаю, будет время загляну в программу — отпишусь:)))
А вот на счет изменений — это и сейчас видно, что все функции зависят одна от другой, у меня сложилось впечатление, что они нанизаны как на нитку с иголкой и если нитку порвать то все разлетится как бусы.
В общем у меня в голове очень много задумок по поводу этой программы, но мало знания и опыта о том как это реализовать. Продолжаем обучение.
To be continued...
СПАСИБО, Череп, за комментарии и за Дейтела, я думаю он мне поможет:)
Кстати, объясни пожалуйста как функция которая возвращает значение
bool
, может вызываться не присваивая возвращаемое значение. В программе это функцияsaveData
, а вызывается она в функцииchangePricesAndBenefits
.На счёт возвращаемого значения (функция
saveData
) тебе Макар вроде ответил. Возвращаемое значение игнорируется.Если посмотреть ассемблерный код (у меня сейчас компилятор TDM-GCC 4.8.1 32-bit, Win), то будет видно, что результат функции возвращается через регистр EAX. В вызывающем коде значение в регистре EAX после вызова функции просто не используется.