Perlu membuat kode saya lebih mudah dibaca oleh programmer lain di tim saya

11

Saya mengerjakan proyek dalam delphi dan saya membuat installer untuk aplikasi, ada Tiga bagian utama.

  1. Instalasi / penghapusan instalasi PostgreSQL
  2. myapplication (setup myapplication dibuat menggunakan instalasi / uninstall nsi).
  3. Membuat tabel di Postgres melalui skrip (file batch).

Setiap hal berjalan dengan baik dan lancar, tetapi jika sesuatu gagal saya telah membuat LogToFileger yang akan LogToFile setiap langkah proses,
seperti ini

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

Fungsi LogToFileToFile.LogToFile()Ini akan menulis konten ke file. Ini berfungsi dengan baik, tetapi masalahnya adalah ini telah mengacaukan kode karena di dalamnya menjadi sulit untuk membaca kode karena satu ca hanya melihat LogToFileToFile.LogToFile()fungsi panggilan di mana-mana dalam kode

sebuah contoh

 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;

seperti yang Anda lihat ada banyak LogToFileToFile.LogToFile()panggilan,
sebelum itu

 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)

ini adalah kasus di seluruh kode saya sekarang.
sulit dibaca.

adakah yang bisa menyarankan saya cara yang bagus untuk mengacaukan panggilan ke LogToFile?

Suka

  1. Mengindentifikasi panggilan 'LogToFileToFile.LogToFile () `
    seperti ini

       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. Unit terpisah seperti UnitLogToFileger
    ini akan memiliki semua pesan LogToFile dalam bentuk switch caseseperti ini

     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;
    

jadi saya bisa memanggil LogToFilegingMyMessage (1) di mana pun diperlukan.

Adakah yang bisa memberi tahu saya mana yang merupakan pendekatan yang lebih baik dan lebih bersih untuk LogToFileging dengan cara ini?

PresleyDias
sumber
5
Untuk menjawab topik Anda: Sudahkah Anda mencoba bertanya kepada tim Anda apakah mereka memahaminya atau apakah semuanya masuk akal? Jika ya, maka itu harus "cukup" dapat dibaca.
Spoike
@Spoike: saya bertanya, agak sulit untuk dibaca, karena di mana-mana logBook.log()ditemui.
PresleyDias
"ada dua bagian utama" bernomor 1 sampai 3. Saya rasa saya mengerti mengapa Anda memiliki pertanyaan tentang keterbacaan. Anda mungkin ingin menemukan seseorang yang dapat "mengedit" untuk konsistensi.
S.Lott
@ S.Lott saya mengedit 'dua' ke 'tiga' ..sory for the
error
Anda mungkin ingin juga mencoba codereview.stackexchange.com
Kirk Broadhurst

Jawaban:

11

Ketika Anda menambahkan logging, Anda memperkenalkan dua hal:

  1. Kode menjadi lebih besar karena untuk hampir setiap tindakan, Anda menambahkan baris yang mencatat tindakan itu (atau kegagalannya)
  2. Garis log itu sendiri tampak membengkak dan menghilangkan keterbacaan karena mengambil banyak ruang.

Masing-masing memiliki masalah memiliki sendiri, solusi yang relatif sederhana:

  1. Pecahkan kode menjadi fungsi yang lebih kecil. Alih-alih memiliki satu fungsi raksasa yang berisi semua salinan Anda serta pesan log untuk kesalahan / kesuksesan, Anda bisa memperkenalkan fungsi "CopyFile", yang akan menyalin tepat satu file dan mencatat hasilnya sendiri. Dengan begitu kode utama Anda hanya terdiri dari panggilan CopyFile dan akan tetap mudah dibaca.

  2. Anda bisa membuat logger Anda lebih pintar. Alih-alih mengirimkan string raksasa yang memiliki banyak informasi berulang, Anda bisa memberikan nilai penghitungan yang akan membuat segalanya lebih jelas. Atau Anda dapat mendefinisikan fungsi Log () yang lebih khusus, yaitu LogFileCopy, LogDbInsert ... Apa pun yang Anda ulangi banyak, pertimbangkan untuk memasukkannya ke dalam fungsinya sendiri.

Jika Anda mengikuti (1), Anda dapat memiliki kode yang terlihat seperti ini:

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

Kemudian CopyFile Anda () hanya perlu beberapa baris kode untuk melakukan tindakan dan mencatat hasilnya, sehingga semua kode Anda tetap ringkas dan mudah dibaca.

Saya akan menjauh dari pendekatan Anda # 2 karena Anda memisahkan informasi yang harus tetap bersama menjadi modul yang berbeda. Anda hanya meminta kode utama Anda agar tidak sinkron dengan pernyataan log Anda. Tetapi melihat LogMyMessage (5), Anda tidak akan pernah tahu itu.

UPDATE (menanggapi komentar): Saya tidak terbiasa dengan bahasa yang Anda gunakan, jadi bagian ini mungkin harus sedikit diadaptasi. Tampaknya semua pesan log Anda mengidentifikasi 3 hal: komponen, tindakan, hasil.

Saya pikir inilah yang disarankan MainMa. Alih-alih meneruskan string aktual, tetapkan konstanta (dalam C / C ++ / C #, mereka akan menjadi bagian dari tipe enum enum). Jadi misalnya untuk komponen, Anda mungkin memiliki: DbInstall, AppFiles, Registry, Shortcuts ... Apa pun yang membuat kode lebih kecil akan membuatnya mudah dibaca.

Ini juga akan membantu jika bahasa Anda mendukung lewat parameter variabel, tidak yakin apakah itu mungkin. Jadi misalnya jika tindakan adalah "FileCopy", Anda bisa menentukan tindakan itu untuk memiliki dua parameter pengguna tambahan: nama file dan direktori tujuan.

Jadi baris penyalinan file Anda akan terlihat seperti ini:

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

* catatan, tidak ada alasan untuk menyalin / menempel baris log dua kali jika Anda dapat menyimpan hasil operasi dalam variabel lokal yang terpisah dan hanya meneruskan variabel itu ke dalam Log ().

Anda melihat temanya di sini, kan? Kurang kode berulang -> kode lebih mudah dibaca.

DXM
sumber
+1, dapatkah Anda memberi tahu saya lebih banyak tentang you could pass in enumerations values ini?
PresleyDias
@PresleyDias: posting yang diperbarui
DXM
ok mendapatkannya, ya kurang berulang-> kode lebih mudah dibaca
PresleyDias
2
+1 "Memecah kode menjadi fungsi yang lebih kecil." Anda tidak bisa cukup menekankan itu. Itu hanya membuat begitu banyak masalah hilang begitu saja.
Oliver Weiler
10

Sepertinya Anda perlu mencabut konsep "LoggableAction". Saya melihat pola dalam contoh Anda di mana semua panggilan mengembalikan bool untuk menunjukkan keberhasilan atau kegagalan dan satu-satunya perbedaan adalah pesan log.

Sudah bertahun-tahun sejak saya menulis delphi jadi ini cukup banyak c # terinspirasi kode semu tapi saya akan berpikir Anda menginginkan sesuatu seperti

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

Kemudian kode panggilan Anda menjadi

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

Saya tidak dapat mengingat sintaks Delphi untuk pointer fungsi tetapi apapun detail implementasi, semacam abstraksi di sekitar log rutin tampaknya menjadi apa yang Anda cari.

MarcE
sumber
Saya mungkin akan pergi dengan cara ini sendiri, tetapi tanpa mengetahui lebih lanjut tentang bagaimana kode OP terstruktur, sulit untuk mengatakan apakah ini akan lebih baik daripada hanya mendefinisikan beberapa metode tambahan untuk memanggil, tanpa menambahkan potensi kebingungan pointer metode (tergantung pada seberapa banyak OP tahu tentang hal-hal seperti itu
S.Robins
+1, LoggableAction()ini bagus, saya bisa langsung menulis nilai yang dikembalikan daripada memeriksa dan menulis.
PresleyDias
saya berharap +100, jawaban yang bagus, tetapi saya hanya dapat menerima satu jawaban :( .. saya akan mencoba saran ini dalam aplikasi saya berikutnya, terima kasih atas idenya
PresleyDias
3

Salah satu pendekatan yang mungkin adalah mengurangi kode dengan menggunakan konstanta.

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');

akan menjadi:

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)

yang memiliki kode log / rasio kode lain yang lebih baik saat menghitung jumlah karakter di layar.

Ini dekat dengan apa yang Anda sarankan dalam poin 2 dari pertanyaan Anda, kecuali bahwa saya tidak akan melangkah sejauh ini: Log(9257)jelas lebih pendek daripada Log(2, SqlInstal, Action, CopyMapSuccess, sOSdrive), tetapi juga cukup sulit untuk dibaca. Apa itu 9257? Apakah ini sukses? Sebuah aksi? Apakah ini terkait dengan SQL? Jika Anda mengerjakan basis kode ini selama sepuluh tahun terakhir, Anda akan belajar angka-angka itu dengan hati (jika ada logika, yaitu 9xxx adalah kode sukses, x2xx terkait dengan SQL, dll.), Tetapi untuk pengembang baru yang menemukan basis kode, kode pendek akan menjadi mimpi buruk.

Anda dapat melangkah lebih jauh dengan mencampurkan kedua pendekatan: gunakan konstanta tunggal. Secara pribadi, saya tidak akan melakukan itu. Ukuran konstanta Anda akan bertambah:

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

atau konstanta akan tetap pendek, tetapi tidak terlalu eksplisit:

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?

Ini juga memiliki dua kelemahan. Anda harus:

  • Simpan daftar terpisah yang mengaitkan info log dengan konstanta masing-masing. Dengan satu konstanta, ia akan tumbuh cepat.

  • Temukan cara untuk menegakkan satu format dalam tim Anda. Misalnya, bagaimana jika alih-alih T2SSQ, seseorang akan memutuskan untuk menulis ST2SQL?

Arseni Mourzenko
sumber
+1, untuk logpanggilan bersih , tetapi bisakah Anda menjelaskan kepada saya lebih banyak hal yang tidak dimengerti Log(2, SqlInstal, Action, CopyMapFailure, sOSdrive), maksud Anda mengatakan SqlInstalakan seperti variabel yang saya tetapkan SqlInstal:=[POSTGRESQL INSTALLATION] ?
PresleyDias
@PresleyDias: SqlInstalbisa berupa apa saja, misalnya nilai 3. Kemudian, dalam Log(), nilai ini akan diterjemahkan secara efektif [POSTGRESQL INSTALLATION]sebelum digabungkan dengan bagian lain dari pesan log.
Arseni Mourzenko
single format in your teamadalah pilihan yang baik / bagus
PresleyDias
3

Cobalah mengekstrak serangkaian fungsi kecil untuk menangani semua hal yang tampak berantakan. Ada banyak kode berulang yang bisa dengan mudah dilakukan di satu tempat. Sebagai contoh:

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;

Caranya adalah dengan melihat duplikasi apa pun dalam kode Anda, dan menemukan cara untuk menghapusnya. Gunakan banyak spasi putih, dan gunakan awal / akhir untuk keuntungan Anda (spasi lebih banyak, dan mudah ditemukan / lipat blok kode). Seharusnya tidak terlalu sulit. Metode-metode ini bisa menjadi bagian dari logger Anda ... terserah Anda. Tapi itu sepertinya tempat yang bagus untuk memulai.

S.Robins
sumber
+1, spasi putih adalah cara yang baik .. success := CopyFile()terima kasih untuk idenya, ini akan mengurangi beberapa baris kode yang tidak perlu dalam kasus saya
PresleyDias
@ S.Robins apakah saya membaca kode Anda dengan benar? metode Anda disebut LogIfFileDoesNotExistmenyalin file?
João Portela
1
@ JoãoPortela Ya ... itu tidak terlalu cantik dan tidak berpegang pada prinsip tanggung jawab tunggal. Ingatlah, ini adalah umpan balik pertama yang keluar dari kepala saya dan bertujuan membantu OP memenuhi tujuannya untuk mengurangi beberapa kekacauan dalam kode-nya. Mungkin ini adalah pilihan nama yang buruk untuk metode ini. Saya akan mengubah sedikit untuk meningkatkan. :)
S.Robins
senang melihat bahwa Anda meluangkan waktu untuk mengatasi masalah itu, +1.
João Portela
2

Saya akan mengatakan bahwa ide di balik opsi 2 adalah yang terbaik. Namun, saya pikir arah yang Anda ambil membuatnya semakin buruk. Bilangan bulat tidak berarti apa-apa. Ketika Anda melihat kode, Anda akan melihat sesuatu sedang dicatat, tetapi Anda tidak tahu apa.

Sebaliknya saya akan melakukan sesuatu seperti ini:

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

Ini mempertahankan struktur pesan tetapi memungkinkan kode Anda menjadi fleksibel. Anda dapat mendefinisikan string konstan sesuai kebutuhan untuk fase dan hanya menggunakan itu sebagai parameter fase. Ini memungkinkan Anda untuk dapat mengubah teks aktual di satu tempat dan mempengaruhi semuanya. Manfaat lain untuk fungsi helper adalah bahwa teks yang penting adalah dengan kode (seperti itu adalah komentar), tetapi teks yang hanya penting untuk file log diabstraksi.

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');
    }
}

Ini bukan sesuatu yang Anda sebutkan dalam pertanyaan Anda, tetapi saya perhatikan tentang kode Anda. Lekukan Anda tidak konsisten. Pertama kali Anda menggunakannya beginbukan indentasi, tetapi yang kedua adalah. Anda melakukan hal serupa dengan else. Saya akan mengatakan ini jauh lebih penting daripada baris log. Ketika lekukan tidak konsisten, itu membuatnya sulit untuk memindai kode dan mengikuti alurnya. Banyak garis log berulang yang mudah disaring saat memindai.

unholysampler
sumber
1

Bagaimana dengan sesuatu di sepanjang baris ini:

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();

Metode NewEntry () akan membangun baris teks (termasuk menambahkan [&] di sekitar entri yang tepat) dan berpendapat bahwa menunggu hingga metode sukses () atau kegagalan () dipanggil, yang menambahkan baris dengan 'sukses' atau 'kegagalan', dan kemudian mengeluarkan baris ke log. Anda juga bisa membuat metode lain, seperti info () untuk saat entri log untuk sesuatu selain keberhasilan / kegagalan.

GrandmasterB
sumber