Нужно сделать мой код более читабельным для других программистов в моей команде

11

Я работаю над проектом в Delphi, и я создаю установщик для приложения, состоит из трех основных частей.

  1. Установка / удаление PostgreSQL
  2. myapplication (настройка myapplication создается с использованием nsi) установка / удаление.
  3. Создание таблиц в Postgres через скрипт (пакетные файлы).

Все работает нормально и гладко, но если что-то не получается, я создал LogToFileger, который будет LogToFile на каждом этапе процесса,
как это

LogToFileToFile.LogToFile('[DatabaseInstallation]  :  [ACTION]:Postgres installation started');

Функция LogToFileToFile.LogToFile()Это запишет содержимое в файл. Это работает хорошо, но проблема в том, что это испортило код, так как стало трудно читать код, так как один может видеть только LogToFileToFile.LogToFile()вызов функции везде в коде

пример

 if Not FileExists(SystemDrive+'\FileName.txt') then
 begin
    if CopyFile(PChar(FilePathBase+'FileName.txt'), PChar(SystemDrive+'\FileName.txt'), False) then
       LogToFileToFile.LogToFile('[DatabaseInstallation] :  copying FileName.txt to '+SystemDrive+'\ done')
       else
       LogToFileToFile.LogToFile('[DatabaseInstallation] :  copying FileName.txt to '+SystemDrive+'\ Failed');
 end;
 if Not FileExists(SystemDrive+'\SecondFileName.txt')      then
   begin
     if CopyFile(PChar(FilePathBase+'SecondFileName.txt'), PChar('c:\SecondFileName.txt'), False) then
       LogToFileToFile.LogToFile('[DatabaseInstallation] : copying SecondFileName.txt to '+SystemDrive+'\ done')
   else
       LogToFileToFile.LogToFile('[DatabaseInstallation] :  copying SecondFileName.txt to '+SystemDrive+'\ Failed');
 end;

как вы можете видеть, есть много LogToFileToFile.LogToFile()звонков,
прежде чем это было

 if Not FileExists(SystemDrive+'\FileName.txt') then
    CopyFile(PChar(FilePathBase+'FileName.txt'), PChar(SystemDrive+'\FileName.txt'), False) 
 if Not FileExists(SystemDrive+'\SecondFileName.txt')      then
   CopyFile(PChar(FilePathBase+'SecondFileName.txt'), PChar('c:\SecondFileName.txt'), False)

это так во всем моем коде сейчас.
его трудно читать.

Кто-нибудь может предложить мне хороший способ, чтобы убрать звонки в LogToFile?

лайк

  1. Отступ в 'LogToFileToFile.LogToFile () `вызов ,
    как это

       if Not FileExists(SystemDrive+'\FileName.txt') then
         begin
             if CopyFile(PChar(FilePathBase+'FileName.txt'), PChar(SystemDrive+'\FileName.txt'), False) then
            {Far away--->>}                   LogToFileToFile.LogToFile(2,'[DatabaseInstallation] :  [ACTION]:copying FileName.txt to '+SystemDrive+'\ sucessful')
       else
            {Far away--->>}                   LogToFileToFile.LogToFile(2,'[DatabaseInstallation] :  [ACTION]:copying FileName.txt to '+SystemDrive+'\ Failed');
       end;
    
  2. Отдельный блок, как LogToFileger
    этот Блок будет иметь все сообщения LogToFile, switch caseкак это

     Function LogToFilegingMyMessage(LogToFilegMessage : integer)
    
     begin
    case  LogToFilegMessage of
    
    1         :  LogToFileToFile.LogToFile(2,'[DatabaseInstallation] :  [ACTION]:copying FileName.txt to '+SystemDrive+'\ sucessful');
    2         :  LogToFileToFile.LogToFile(2,'[DatabaseInstallation] :  [ACTION]:copying FileName.txt to '+SystemDrive+'\ Failed');
       150        :  LogToFileToFile.LogToFile(2,'[somthing] :  [ACTION]: somthing important);
    
    end;
    

так что я могу просто вызвать LogToFilegingMyMessage (1), где это необходимо.

Может кто-нибудь сказать мне, что является лучшим и более чистым подходом к LogToFileging таким образом?

PresleyDias
источник
5
Чтобы ответить на вашу тему: Вы пытались спросить свою команду, понимают ли они это или все это имеет смысл? Если да, то он должен быть «достаточно» читабельным.
Спойк
@ Спойк: я спросил, его немного трудно читать, так как везде logBook.log()встречаются.
PresleyDias
«Есть две основные части» с номерами от 1 до 3. Я думаю, я понимаю, почему у вас есть вопрос о читабельности. Возможно, вы захотите найти кого-то, кто может «редактировать» для согласованности.
С.Лотт
@ S.Lott я отредактировал «два» на «три» .. извините за ошибку
PresleyDias
Вы можете также попробовать codereview.stackexchange.com
Кирк Бродхерст

Ответы:

11

Когда вы добавили запись в журнал, вы ввели две вещи:

  1. Код стал больше, потому что почти для каждого действия вы добавили строку, которая регистрирует это действие (или его неудачу)
  2. Сами строки журнала кажутся раздутыми и лишают возможности чтения, потому что они занимают так много места.

У каждой из этих проблем есть свое, относительно простое решение:

  1. Разбейте код на более мелкие функции. Вместо одной гигантской функции, которая содержит все ваши копии, а также регистрирует сообщения об ошибках / успехах, вы можете ввести функцию «CopyFile», которая будет копировать ровно один файл и регистрировать свой собственный результат. Таким образом, ваш основной код будет состоять из вызовов CopyFile и будет легко читаться.

  2. Вы могли бы сделать свой регистратор умнее. Вместо того, чтобы передавать гигантскую строку, содержащую много повторяющейся информации, вы можете передать значения перечислений, которые прояснят ситуацию. Или вы можете определить более специализированные функции Log (), например, LogFileCopy, LogDbInsert ... Что бы вы ни повторяли много раз, рассмотрите возможность включения этого в свою собственную функцию.

Если вы будете следовать (1), вы можете получить код, который выглядит следующим образом:

CopyFile( sOSDrive, 'Mapannotation.txt' )
CopyFile( sOSDrive, 'Mappoints.txt' )
CopyFile( sOSDrive, 'Mapsomethingelse.txt' )
. . . .

Тогда вашему CopyFile () нужно всего несколько строк кода, чтобы выполнить действие и записать его результат, поэтому весь ваш код остается кратким и легким для чтения.

Я бы держался подальше от вашего подхода № 2, поскольку вы разделяете информацию, которая должна оставаться вместе, в разные модули. Вы просто просите, чтобы ваш основной код не синхронизировался с вашими лог-операторами. Но, глядя на LogMyMessage (5), вы никогда не узнаете этого.

ОБНОВЛЕНИЕ (ответ на комментарий): я не знаю, какой именно язык вы используете, поэтому эту часть, возможно, придется немного адаптировать. Кажется, все ваши сообщения журнала идентифицируют 3 вещи: компонент, действие, результат.

Я думаю, что это в значительной степени то, что предложил MainMa. Вместо передачи фактической строки определите константы (в C / C ++ / C # они будут частью перечислимого типа). Например, для компонентов у вас могут быть: DbInstall, AppFiles, Registry, Shortcuts ... Все, что делает код меньше, облегчит чтение.

Также было бы полезно, если бы ваш язык поддерживал передачу параметров переменных, но не уверен, что это возможно. Так, например, если действие «FileCopy», вы можете определить это действие, чтобы иметь два дополнительных пользовательских параметра: имя файла и каталог назначения.

Таким образом, строки для копирования вашего файла будут выглядеть примерно так:

Bool isSuccess = CopyFile(PChar(sTxtpath+'Mapannotation.txt'), PChar(sOSdrive+'\Mapannotation.txt'), False)
LogBook.Log( DbInstall, FileCopy, isSuccess, 'Mapannotation.txt', sOSDrive )

* обратите внимание, также нет причин дважды копировать / вставлять строку журнала, если вы можете сохранить результат операции в отдельной локальной переменной и просто передать эту переменную в Log ().

Вы видите тему здесь, верно? Менее повторяющийся код -> более читаемый код.

DXM
источник
+1, ты можешь рассказать мне больше об you could pass in enumerations values этом?
PresleyDias
@PresleyDias: обновленное сообщение
DXM
хорошо, понял, да менее повторяющийся-> более читаемый код
PresleyDias
2
+1 "Разбейте код на более мелкие функции." Вы не можете подчеркнуть это достаточно. Это просто заставляет так много проблем исчезать.
Оливер Вейлер
10

Похоже, вам нужно абстрагироваться от концепции «LoggableAction». В вашем примере я вижу шаблон, в котором все вызовы возвращают логическое значение, указывающее на успех или неудачу, и единственное отличие - это сообщение журнала.

Прошло много лет с тех пор, как я написал Delphi, так что это в значительной степени вдохновленный c # псевдокод, но я бы подумал, что вы хотите что-то вроде

void LoggableAction(FunctionToCallPointer, string logMessage)
{
    if(!FunctionToCallPointer)
    {  
        Log(logMessage).
    }
}

Тогда ваш код вызова становится

if Not FileExists(sOSdrive+'\Mapannotation.txt') then
    LoggableAction(CopyFile(PChar(sTxtpath+'Mapannotation.txt'), "Oops, it went wrong")

Я не могу вспомнить синтаксис Delphi для указателей на функции, но какими бы ни были детали реализации, какая-то абстракция вокруг подпрограммы журнала может показаться вам нужной.

Marce
источник
Я бы, вероятно, пошел бы этим путем сам, но, не зная больше о том, как структурирован код OP, трудно сказать, будет ли это лучше, чем просто определить пару дополнительных методов для вызова, не добавляя потенциальную путаницу указателей методов (в зависимости от о том, сколько ОП знает о таких вещах.
С.Робинс
+1, LoggableAction()это хорошо, я могу напрямую написать возвращаемое значение вместо проверки и записи.
PresleyDias
Я хочу +100, отличный ответ, но я могу принять только один ответ :( .. Я попробую это предложение в моем следующем приложении, спасибо за идею
PresleyDias
3

Одним из возможных подходов является сокращение кода с помощью констант.

if CopyFile(PChar(sTxtpath+'Mapannotation.txt'), PChar(sOSdrive+'\Mapannotation.txt'), False) then
   LogBook.Log(2,'[POSTGRESQL INSTALLATION] :  [ACTION]:copying Mapannotation.txt to '+sOSdrive+'\ sucessful')
   else
   LogBook.Log(2,'[POSTGRESQL INSTALLATION] :  [ACTION]:copying Mapannotation.txt to '+sOSdrive+'\ Failed');

станет:

if CopyFile(PChar(sTxtpath+'Mapannotation.txt'), PChar(sOSdrive+'\Mapannotation.txt'), False) then
   Log(2, SqlInstal, Action, CopyMapSuccess, sOSdrive)
   else
   Log(2, SqlInstal, Action, CopyMapFailure, sOSdrive)

который имеет лучший лог-код / ​​другое соотношение кодов при подсчете количества символов на экране.

Это близко к тому, что вы предложили в пункте 2 вашего вопроса, за исключением того, что я бы не пошел так далеко: Log(9257)он явно короче Log(2, SqlInstal, Action, CopyMapSuccess, sOSdrive), но также довольно сложен для чтения. Что такое 9257? Это успех? Действие? Это связано с SQL? Если вы работаете над этой кодовой базой в течение последних десяти лет, вы узнаете эти цифры наизусть (если есть логика, то есть 9xxx - это коды успеха, x2xx относятся к SQL и т. Д.), Но для начинающего разработчика, который обнаруживает кодовая база, короткие коды будут кошмаром.

Вы можете пойти дальше, смешав два подхода: использовать одну константу. Лично я бы не стал этого делать. Либо ваши константы будут расти в размерах:

Log(Type2SuccessSqlInstallCopyMapSuccess, sOSdrive) // Can you read this? Really?

или константы останутся короткими, но не очень явными:

Log(T2SSQ_CopyMapSuccess, sOSdrive) // What's T2? What's SSQ? Or is it S, followed by SQ?
// or
Log(CopyMapSuccess, sOSdrive) // Is it an action? Is it related to SQL?

Это также имеет два недостатка. Вам придется:

  • Держите отдельный список, связывающий информацию журнала с их соответствующими константами. С одной константой он будет быстро расти.

  • Найдите способ применения единого формата в вашей команде. Например, что если вместо этого T2SSQкто-то решит написать ST2SQL?

Арсений Мурзенко
источник
+1, для чистого logвызова, но можете ли вы объяснить мне больше, что он не понял Log(2, SqlInstal, Action, CopyMapFailure, sOSdrive), вы хотите сказать, SqlInstalбудет ли моя определенная переменная как SqlInstal:=[POSTGRESQL INSTALLATION] ?
PresleyDias
@PresleyDias: SqlInstalможет быть чем угодно, например значением 3. Затем, в Log(), это значение будет эффективно преобразовано в, [POSTGRESQL INSTALLATION]прежде чем будет объединено с другими частями сообщения журнала.
Арсений Мурзенко
single format in your teamхороший / отличный вариант
PresleyDias
3

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

procedure CopyIfFileDoesNotExist(filename: string);
var
   success: boolean;
begin
   if Not FileExists(sOSdrive+'\'+filename') then
   begin
      success := CopyFile(PChar(sTxtpath+filename), PChar(sOSdrive+filename), False);

      Log(filename, success);
   end;
end;

procedure Log(filename: string; isSuccess: boolean)
var
   state: string;
begin
   if isSuccess then
   begin
      state := 'success';
   end
   else
   begin
      state := 'failed';
   end;

   LogBook.Log(2,'[POSTGRESQL INSTALLATION] : [ACTION]:copying ' + filename + ' to '+sOSdrive+'\ ' + state);
end;

Хитрость заключается в том, чтобы посмотреть на любое дублирование в вашем коде и найти способы его устранения. Используйте много пробелов и используйте начало / конец в ваших интересах (больше пробелов и легко найти / сложить блоки кода). Это действительно не должно быть слишком сложно. Эти методы могут быть частью вашего регистратора ... это зависит от вас на самом деле. Но это выглядит как хорошее место для начала.

S.Robins
источник
+1, пробелы - хороший способ ... success := CopyFile()спасибо за идею, это уменьшит некоторые ненужные строки кода в моем случае
PresleyDias
@ S.Robins, я правильно прочитал твой код? твой метод называется LogIfFileDoesNotExistкопирование файлов?
Жоао Портела
1
@ JoãoPortela Да ... это не очень красиво и не придерживается принципа единой ответственности. Имейте в виду, что это был первый проход рефакторинга на макушке моей головы, и он был направлен на то, чтобы помочь ОП выполнить его задачу по уменьшению помех в его коде. Вероятно, это плохой выбор названия метода. Я немного подправлю, чтобы улучшить. :)
S.Robins
приятно видеть, что вы нашли время для решения этой проблемы, +1.
Жоао Портела
2

Я бы сказал, что идея, лежащая в основе варианта 2, является лучшей. Тем не менее, я думаю, что направление, которое вы выбрали, ухудшает ситуацию. Целое число ничего не значит. Когда вы посмотрите на код, вы увидите, что что-то регистрируется, но вы не знаете что.

Вместо этого я бы сделал что-то вроде этого:

void logHelper(String phase, String message) {
   LogBook.Log(2, "[" + phase + "] :  [Action]: " + message);
}

Это сохраняет структуру сообщения, но позволяет вашему коду быть гибким. Вы можете определить постоянные строки по мере необходимости для фаз и использовать их только в качестве параметра фазы. Это позволяет вам вносить изменения в фактический текст в одном месте и влиять на все. Другое преимущество вспомогательной функции заключается в том, что важный текст связан с кодом (как это был комментарий), но текст, который важен только для файла журнала, абстрагируется.

if (!FileExists(sOSdrive+'\Mapannotation.txt')) {
    if (CopyFile(PChar(sTxtpath+'Mapannotation.txt'), PChar(sOSdrive+'\Mapannotation.txt'), False)) {
       logHelper(POSTGRESQL, 'copying Mapannotation.txt to '+ sOSdrive +'\ sucessful')
    } else {
       logHelper(POSTGRESQL, 'copying Mapannotation.txt to '+ sOSdrive +'\ Failed');
    }
}

Это не то, что вы упомянули в своем вопросе, но я заметил о вашем коде. Ваш отступ не соответствует. Первый раз, когда вы используете beginего, не отступ, а второй раз. Вы делаете то же самое с else. Я бы сказал, что это гораздо важнее, чем строки журнала. Если отступы не согласованы, это затрудняет сканирование кода и отслеживание потока. Многие повторяющиеся строки журнала легко фильтруются при сканировании.

unholysampler
источник
1

Как насчет этого:

LogBook.NewEntry( 2,'POSTGRESQL INSTALLATION', 'copying Mapannotation.txt to '+sOSdrive);

if CopyFile(PChar(sTxtpath+'Mapannotation.txt'), PChar(sOSdrive+'\Mapannotation.txt'), False) then
    LogBook.Success()
else
    LogBook.Failed();

Метод NewEntry () будет строить строку текста (включая добавление [&] вокруг правильных записей) и удерживать ее в ожидании вызова методов success () или fail (), которые добавляют строку с «success» или «сбой», а затем выведите строку в журнал. Вы также можете использовать другие методы, например, info (), когда запись в журнале предназначена для чего-то другого, кроме успеха / неудачи.

GrandmasterB
источник