Потрібно зробити свій код більш читабельним для інших програмістів у моїй команді


11

Я працюю над проектом у delphi, і я створюю інсталятор програми, є три основні частини.

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

Кожна річ працює нормально, але якщо щось не вдається, я створив LogToFileger, який буде LogToFile кожен крок процесу,
як це

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

Функція LogToFileToFile.LogToFile()Це запише вміст у файл. Це добре працює, але проблема полягає в тому, що це зіпсувало код, оскільки в ньому стало важко читати код, оскільки один ca може бачити лише 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?


5
Щоб відповісти на вашу тему: Ви намагалися запитати свою команду, чи вони її розуміють, чи це має сенс? Якщо так, то воно повинно бути "достатньо" читабельним.
Спайк

@Spoike: я запитав, його важко читати, як скрізь logBook.log()зустрічається.
PresleyDias

"Є дві основні частини", пронумеровані від 1 до 3. Я думаю, я бачу, чому у вас виникає питання про читабельність. Ви можете знайти когось, хто може "редагувати" для послідовності.
S.Lott

@ 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, реєстр, ярлики ... Все, що робить код меншим, зробить його легким для читання.

Це також допоможе, якщо ваша мова підтримує змінний параметр, не впевнений, чи це можливо. Так, наприклад, якщо дія "FileCopy", ви можете визначити, що ця дія має два додаткові параметри користувача: ім'я файлу та каталог призначення.

Отже, рядки для копіювання файлів виглядатимуть приблизно так:

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

* Зауважте, також немає причин копіювати / вставляти рядок журналу двічі, якщо ви можете зберегти результат операції в окремій локальній змінній і просто передати цю змінну в Log ().

Ви бачите тему тут, правда? Менш повторюваний код -> більш читабельний код.


+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 для вказівників функцій, але які б не були деталі реалізації, якась абстракція навколо журналу журналу, здавалося б, саме те, що ви шукаєте.


Я б, мабуть, пішов самим цим шляхом, але не знаючи більше про те, як структурується код ОП, важко сказати, чи було б це краще, ніж просто визначити пару додаткових методів для виклику, не додаючи потенційну плутанину вказівників методів (залежно про те, наскільки ОП знає про такі речі.
Робінз

+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;

Хитрість полягає в тому, щоб переглянути будь-яке дублювання вашого коду та знайти способи його видалення. Використовуйте багато пробілів, і використовуйте початок / кінець на вашу користь (більше пробілів та легкий пошук / складання блоків коду). Це дійсно не повинно бути занадто важким. Ці методи можуть бути частиною вашого реєстратора ... це вирішувати саме вам. Але це виглядає як гарне місце для початку.


+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. Я б сказав, що це набагато важливіше, ніж рядки журналів. Якщо відступ не узгоджується, це ускладнює сканування коду та стеження за потоком. Багато повторюваних ліній журналу легко відфільтрувати під час сканування.


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 () створив би рядок тексту (включаючи додавання [&] навколо правильних записів) і затримав би те, що очікують, поки будуть викликані методи успіху () або невдачі (), які додають рядок "успіх" або 'fail', а потім виведіть рядок у журнал. Ви також можете скористатися іншими методами, такими як info () для того, коли запис журналу - це щось інше, ніж успіх / неудача.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.