Skip to content

Comments

Hw2#2

Open
TimofeyTst wants to merge 15 commits intomainfrom
hw2
Open

Hw2#2
TimofeyTst wants to merge 15 commits intomainfrom
hw2

Conversation

@TimofeyTst
Copy link
Owner

No description provided.

@TimofeyTst TimofeyTst requested a review from 0x0000dead March 29, 2023 19:03
@TimofeyTst
Copy link
Owner Author

Единственное не могу понять: Логику методов класса из файла CommandParser.hpp я вынес отдельно в файл CommanParser.cpp, в котором сделал #include "CommandParser.hpp", и у меня все отлично компилируется.
Я захотел вынести логику методов класса IOperation в отдельный файл IOperation.cpp, но когда я создаю src/IOperation.cpp и делаю первой строкой #include "IOperation.hpp" то файл не видит такого заголовочного файла, хотя он есть. Вроде мой makefile учитывает все эти заголовочные файлы, но почему так происходит я не мог понять, мучился около часа с этим, даже проигнорировал сообщение от vscode о том, что нет связи, просто вручную перенес в этот файл логику, но компиляция не прошла, почему такое может быть?

@0x0000dead
Copy link
Collaborator

Скорее всего, потому что у тебя поиск hpp файла происходит в директории src, а он в "../include/*.hpp". Можешь попробовать использовать CMakeLists.txt - кроссплатформенно и понятнее

@TimofeyTst
Copy link
Owner Author

Скорее всего, потому что у тебя поиск hpp файла происходит в директории src, а он в "../include/*.hpp". Можешь попробовать использовать CMakeLists.txt - кроссплатформенно и понятнее

Не знаю что я сделал, но оно заработало. Перенес логику hpp в cpp

Copy link
Collaborator

@0x0000dead 0x0000dead left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merge конфликт какой-то, а так по логике задания вроде нормально

@TimofeyTst TimofeyTst requested a review from 0x0000dead April 3, 2023 12:50
@TimofeyTst TimofeyTst requested a review from 0x0000dead April 5, 2023 19:33
Copy link
Collaborator

@0x0000dead 0x0000dead left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, только конфликт с README

@TimofeyTst TimofeyTst requested a review from ikramichev April 12, 2023 10:36
Sending prev pipeline to next operation
list.RunOperations();
}
catch(const std::exception& e){
std::cerr << e.what() << '\n';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

retrun 1; (раз какая-то ошибка)

#include <memory>
#include <string>
#include "IOperation.hpp"
#include "EchoOperation.hpp"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CommandParser.hpp-заголовку не нужно знать, какие именно конкретные операции есть в программе. А заодно эти операции не нужны main.cpp, который инклудит этот CommandParser.hpp. Реально, эти заголовки нужны только CommandParser.cpp. Значит, следует туда и перенести. Это уменьшит зависимость внутри проекта, т.е. например, при добавлении нового оператора, main.cpp не нужно будет пересобирать. Т.е. скорость сборки увеличивает, размер программы итоговой уменьшает. В идеале сущности должны видеть только то, что действительно используют. Это касается и других заголовков: разного рода memory и iostream. Если конкретно здесь оно не используется - значит можно удалять. iostream, в этом файле тоже не нужен.


private:
const std::string pipeline_;
const std::string delimiter = " | ";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нет смысла хранить эту константу внутри класса. Ведь когда мы создаем экземпляр этого класса, эта константа будет частью получившегося объекта. И если мы создадим 2 экземпляра, то константа будет продублирована. Лучше всего этот delimeter отправить в cpp файл, где он используется, и там его можно или его вытащить в глобальные константы, или проинициализировать внутри какой-то функции, но сделаеть ее static. Оба этих варианта исключат дублирование константы в коде.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Просто сделаю ее статичной внутри класса. Тогда дублирования не будет, верно?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Верно, не будет. Но если выбирать, где лучше сделать ее статической: статическим полем класса или статической локальной переменной в одной каком-то методе в cpp-файле, то я бы выбрал второе. В этом случае мы как бы больше "скрываем" деталей реализации от других. То есть следуем правилу, по которому сущности должны видеть только то, что используют, и то, что им необходимо видеть. На практике это могло бы пригодиться в такой ситуации: решили изменить значение этого разделителя, заменили | на какой-нибудь /. Если константа определена в cpp-файле, то main.cpp перекомпилировать не пришлось бы, тк с его точки зрения, ничего не изменилось, тк он и не видел ничего.


class OperationsList {
public:
OperationsList() : pipeline_(), head_(), tail_() {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Если такой конструктор не используется в коде, то и не нужно его явно определять. То есть я бы удалил.
Даже если бы такой вариант конструктора был бы востребован, то я бы удалил лист инициализации после двоеточия. Ведь каждый из этих параметров имеет известный нам конструктор по умолчанию, который не нужно явно вызывать, и значения по умолчанию всех 3х полей нам известны.

OperationsList::OperationsList(const std::string& input) : pipeline_(input), head_(nullptr), tail_(nullptr) {
size_t pos = 0;
std::string token;
std::string_view cursor(pipeline_);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я вижу, что pipeline_ используется только 1 раз, в конструкторе. Если так, то зачем его хранить, тратить место, и ломать голову, зачем он нужен. Не стоит добавлять в класс методы и поля "на будущее". Если они не используются - то надо просто удалить. Так код становится более читаемым и оптимальным.

@@ -0,0 +1,66 @@
#include "CommandParser.hpp"

OperationsList::OperationsList(const std::string& input) : pipeline_(input), head_(nullptr), tail_(nullptr) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я бы не стал явно инициализировать поля, для которых мы и так знаем, что делает конструктор по умолчанию. Т.е. head_ и tail_ и так понятно, что будут нулями. Можно убрать из листа явной инициализации. (Другое дело, когда мы имеем дело, с какими-нибудь интами, и там не опредлено, какое значение будет в поле, если его явно не инициализировали, да потом еще и обратились к ней, не проинциализировав.)

}

void OperationsList::AddOperation(const std::string& token){
const std::string ECHO = "echo";
Copy link
Collaborator

@ikramichev ikramichev Apr 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вот эти константы лучше или отправить вне функции просто наверх, или оставить здесь, но static. Так они не будут создаваться и удаляться при каждой AddOperation.

А еще лучше не использовать капслок для обычных констант. Это похоже на дефайны, но все-таки не дефайны.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А какие константы считаются необычными?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Неверно выразился. Я имею в виду любые константы. В противовес старым дефайнам. Раньше в Сишном коде повсюду были дефайны и разные макросы. Сейчас уже язык богаче, возможностей больше, и использовать старомодные дефайны считается "моветоном". А капслок в названии - это некое подражание старому стилю. Но его использовать сегодня незачем, капслок - только для дефайнов, а вместо дефайнов теперь константы.


// Реализуем по умолчанию метод интерфейсного класса
void IOperation::SetNextOperation(std::shared_ptr<IOperation>&& operation) {
next_operation_ = std::move(operation);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@@ -0,0 +1,17 @@
#pragma once
#include <iostream>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вот здесь и во многих других местах, по-хорошему нужно убрать или перенести хедеры в файлы, где они действительно нужны. В этом файле, кстати, нужен только IOperation.hpp, все остальное или уже определено в IOperation.hpp или нужно, но не здесь, а например, в CatOperation.cpp

class CatOperation final : public IOperation {
public:
CatOperation() : IOperation() {}
CatOperation(std::string& filename, std::shared_ptr<IOperation>&& operation=nullptr) : IOperation(std::move(operation)), filename_(filename) {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

По-моему в коде многовато "лишних" конструкторов. Например, кажется, что не нужен обычный конструктор у CatOperation(). Не вижу, где используется. Так же "хитрый" второй конструктор, с дефолтное некст-операцией, вроде тоже не нужен такой. Если где-то что-то не используется, то следует подчистить.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я имею в виду: а есть ли хотя бы одно место, где этот конструктор используется с двумя параметрами? Если нет, то нужно оставить только один параметр filename и дело с концом. Кстати, почему он не const std::string& ?
Я имею в виду, что если что-то не используется, то оно должно быть удалено.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А если мне потом понадобится передать сразу при конструкторе следующую операцию? Я буду вынужден залезать в код и редактировать. Зачем? Сразу надо предусматривать удобство

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Обычно при таком подходе происходит немного не так:

  1. Код так и остается неиспользованным.
  2. Когда действительно понадобится что-то поменять, то скорее всего не именно здесь, а немного рядом (например, действительно потребуется второй параметр, но другой), и в этом случае придется не только добавлять второй нужный параметр, но и продолжать поддерживать ненужный третий.
  3. Читается такой код хуже. Будет потрачено время на анализ, что это и для чего. (не обязательно к этому случаю, а просто к любому неиспользумому коду). Если потребуется код "переписать", то много усилий стоит понять, для чего же нужен код, который не используется, и можно ли его просто удалить.
  4. Работает ли правильно неиспользуемый код - большой вопрос. Можно ли его просто взять и использовать. Я имею в виду случай, когда программа давно живет "в продакшне", и в ней нужно что-то починить или добавить. Используемый код так или иначе проверен реальной работой.
  5. Если оставить, допустим, такую возможность в конструкторе. То почему не добавить еще несколько конструкторов? Если мы пишем библиотеку, вроде STL, то, конечно, нам надо предоставить все виды конструкторов, которые могли бы быть полезны пользователю. Но в нашем случае мы пишем прикладную программу.

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


// создаем std::stringstream и передаем в него буфер файла
std::stringstream file_stream;
file_stream << file_.rdbuf();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 слова о подводных камнях.
Вижу, что этот вариант cat-а читает файл целиком, а потом целиком отправляет его дальше. Настоящий линуксовый кат, читает файл все-таки построчно, и построчно отправляет его на вывод. Благодаря этой особенности, можно дампить гигантские файлы, или вообще обрабатывать гигантские файлы, например грепом, построчно. Если прочитать слишком большой файл, то можно "надорваться".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants