Cần làm cho mã của tôi dễ đọc hơn đối với các lập trình viên khác trong nhóm của tôi


11

Tôi đang làm việc với một dự án trong delphi và tôi đang tạo một trình cài đặt cho ứng dụng, có ba phần chính.

  1. Cài đặt / gỡ cài đặt PostgreSQL
  2. myapplication (thiết lập myapplication được tạo bằng nsi) cài đặt / gỡ cài đặt.
  3. Tạo bảng trong Postgres thông qua tập lệnh (tệp bó).

Mọi thứ đều chạy tốt và trơn tru, nhưng nếu có lỗi, tôi đã tạo một LogToF đặc biệt sẽ LogToFile mỗi bước của quy trình,
như thế này

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

Các chức năng LogToFileToFile.LogToFile()này sẽ ghi nội dung vào một tập tin. Điều này đang hoạt động tốt, nhưng vấn đề là điều này đã làm rối mã vì trong đó việc đọc mã trở nên khó khăn vì một ca chỉ nhìn thấy LogToFileToFile.LogToFile()hàm gọi ở mọi nơi trong mã

một ví dụ

 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;

như bạn có thể thấy có rất nhiều LogToFileToFile.LogToFile()cuộc gọi,
trước đó là

 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)

đây là trường hợp trong toàn bộ mã của tôi bây giờ
nó khó đọc

có ai có thể gợi ý cho tôi một cách hay để giải nén các cuộc gọi tới LogToFile không?

giống

  1. Thụt vào cuộc gọi 'LogToFileToFile.LogToFile () `
    như thế này

       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. Đơn vị riêng biệt như LogToFileger
    Đơn vị này sẽ có tất cả các thông điệp LogToFile switch casegiống như thế này

     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;
    

vì vậy tôi chỉ có thể gọi LogToFilegingMyMessage (1) nếu cần.

Ai đó có thể cho tôi biết cách tiếp cận nào tốt hơn và rõ ràng hơn đối với LogToFileging theo cách này không?


5
Để trả lời chủ đề của bạn: Bạn đã thử hỏi nhóm của bạn nếu họ hiểu nó hoặc nếu tất cả có ý nghĩa? Nếu có, thì nó sẽ "đủ" có thể đọc được.
Spoike

@Spoike: tôi đã hỏi, nó hơi khó đọc, vì mọi nơi logBook.log()đều gặp phải.
PresleyDias

"Có hai phần chính" được đánh số từ 1 đến 3. Tôi nghĩ rằng tôi hiểu tại sao bạn có một câu hỏi về khả năng đọc. Bạn có thể muốn tìm một người có thể "chỉnh sửa" cho thống nhất.
S.Lott

@ S.Lott tôi đã chỉnh sửa 'hai' thành 'ba' .. lý do cho lỗi lầm
PresleyDias

Bạn cũng có thể muốn dùng thử codereview.stackexchange.com
Kirk Broadhurst

Câu trả lời:


11

Khi bạn thêm đăng nhập, bạn đã giới thiệu hai điều:

  1. Mã trở nên lớn hơn bởi vì đối với hầu hết mọi hành động, bạn đã thêm một dòng ghi nhật ký hành động đó (hoặc thất bại của nó)
  2. Bản thân các dòng nhật ký dường như bị cồng kềnh và mất khả năng đọc vì chúng chiếm quá nhiều chỗ.

Mỗi vấn đề có một giải pháp riêng, tương đối đơn giản:

  1. Phá mã thành các chức năng nhỏ hơn. Thay vì có một chức năng khổng lồ chứa tất cả các bản sao của bạn cũng như thông báo nhật ký lỗi / thành công, bạn có thể giới thiệu một chức năng "CopyFile", sẽ sao chép chính xác một tệp và ghi lại kết quả của chính nó. Bằng cách đó, mã chính của bạn sẽ chỉ bao gồm các lệnh gọi CopyFile và sẽ vẫn dễ đọc.

  2. Bạn có thể làm cho logger của bạn thông minh hơn. Thay vì truyền vào một chuỗi khổng lồ có nhiều thông tin lặp đi lặp lại, bạn có thể chuyển vào các giá trị liệt kê sẽ làm cho mọi thứ rõ ràng hơn. Hoặc bạn có thể định nghĩa các hàm Log () chuyên dụng hơn, ví dụ LogFileCopy, LogDbInsert ... Bất kể bạn lặp lại nhiều điều gì, hãy xem xét việc đưa vào chức năng của chính nó.

Nếu bạn làm theo (1), bạn có thể có mã giống như thế này:

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

Sau đó, CopyFile () của bạn chỉ cần vài dòng mã để thực hiện hành động và ghi lại kết quả của nó, vì vậy tất cả mã của bạn vẫn ngắn gọn và dễ đọc.

Tôi sẽ tránh xa cách tiếp cận số 2 của bạn vì bạn đang tách thông tin nên ở lại với nhau thành các mô-đun khác nhau. Bạn chỉ yêu cầu mã chính của bạn để không đồng bộ với các báo cáo nhật ký của bạn. Nhưng nhìn vào LogMyMessage (5), bạn sẽ không bao giờ biết điều đó.

CẬP NHẬT (phản hồi bình luận): Tôi không quen với ngôn ngữ chính xác mà bạn đang sử dụng, vì vậy phần này có thể phải được điều chỉnh một chút. Dường như tất cả các thông điệp tường trình của bạn xác định 3 điều: thành phần, hành động, kết quả.

Tôi nghĩ rằng đây là khá nhiều những gì MainMa đề xuất. Thay vì chuyển chuỗi thực tế, hãy xác định các hằng số (trong C / C ++ / C #, chúng sẽ là một phần của kiểu liệt kê liệt kê). Vì vậy, ví dụ cho các thành phần, bạn có thể có: DbInstall, AppFiles, Registry, Shortcut ... Bất cứ điều gì làm cho mã nhỏ hơn sẽ giúp bạn dễ đọc.

Nó cũng sẽ giúp nếu ngôn ngữ của bạn hỗ trợ tham số biến, không chắc chắn nếu điều đó có thể. Vì vậy, ví dụ nếu hành động là "FileCopy", bạn có thể xác định hành động đó để có thêm hai tham số người dùng: tên tệp và thư mục đích.

Vì vậy, các dòng sao chép tập tin của bạn sẽ trông giống như thế này:

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

* lưu ý, cũng không có lý do gì để sao chép / dán dòng nhật ký hai lần nếu bạn có thể lưu trữ kết quả của hoạt động trong một biến cục bộ riêng biệt và chỉ cần chuyển biến đó vào Log ().

Bạn thấy chủ đề ở đây, phải không? Mã ít lặp lại -> mã dễ đọc hơn.


+1, bạn có thể cho tôi biết thêm về you could pass in enumerations values điều này?
PresleyDias

@PresleyDias: bài đăng được cập nhật
DXM

ok hiểu rồi, vâng, ít lặp đi lặp
lại-

2
+1 "Chia mã thành các hàm nhỏ hơn." Bạn không thể nhấn mạnh đến thế. Nó chỉ làm cho rất nhiều vấn đề chỉ biến mất.
Oliver Weiler

10

Có vẻ như bạn cần trừu tượng hóa khái niệm "LoggableAction". Tôi đang thấy một mẫu trong ví dụ của bạn trong đó tất cả các cuộc gọi trả về một bool để biểu thị thành công hay thất bại và sự khác biệt duy nhất là thông điệp tường trình.

Đã nhiều năm kể từ khi tôi viết delphi, vì vậy đây là mã giả được lấy cảm hứng từ c # nhưng tôi nghĩ bạn muốn một cái gì đó như

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

Sau đó, mã cuộc gọi của bạn trở thành

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

Tôi không thể nhớ cú pháp Delphi cho các con trỏ hàm nhưng cho dù chi tiết triển khai là gì, một số trừu tượng xung quanh thói quen nhật ký dường như là thứ bạn đang tìm kiếm.


Có lẽ tôi sẽ tự đi theo cách này, nhưng không biết thêm về cách cấu trúc mã của OP, thật khó để biết liệu điều này có tốt hơn không chỉ đơn giản là xác định một vài phương thức bổ sung để gọi, mà không thêm sự nhầm lẫn tiềm ẩn của con trỏ phương thức (tùy thuộc OP biết bao nhiêu về những điều như vậy.
S.Robins

+1, LoggableAction()điều này thật tuyệt, tôi có thể viết trực tiếp giá trị trả về thay vì kiểm tra và viết.
PresleyDias

Tôi ước +100, câu trả lời tuyệt vời, nhưng tôi chỉ có thể chấp nhận một câu trả lời :( .. tôi sẽ thử đề xuất này trong ứng dụng tiếp theo của mình, cảm ơn vì ý tưởng này
PresleyDias

3

Một cách tiếp cận có thể là giảm mã bằng cách sử dụng hằng.

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

sẽ trở thành:

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)

có mã nhật ký / tỷ lệ mã khác tốt hơn khi đếm số lượng ký tự trên màn hình.

Điều này gần với những gì bạn đề xuất trong điểm 2 của câu hỏi của bạn, ngoại trừ việc tôi sẽ không đi quá xa: Log(9257)rõ ràng là ngắn hơn Log(2, SqlInstal, Action, CopyMapSuccess, sOSdrive), nhưng cũng khá khó đọc. 9257 là gì? Đó có phải là một thành công? Một hành động? Có liên quan đến SQL không? Nếu bạn làm việc trên cơ sở mã này trong mười năm qua, bạn sẽ học được những con số đó (nếu có logic, tức là 9xxx là mã thành công, x2xx có liên quan đến SQL, v.v.), nhưng đối với một nhà phát triển mới phát hiện ra codebase, mã ngắn sẽ là một cơn ác mộng.

Bạn có thể đi xa hơn bằng cách trộn hai cách tiếp cận: sử dụng một hằng số duy nhất. Cá nhân, tôi sẽ không làm điều đó. Hằng số của bạn sẽ tăng kích thước:

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

hoặc các hằng số sẽ vẫn ngắn, nhưng không rõ ràng lắm:

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?

Điều này cũng có hai nhược điểm. Bạn sẽ phải:

  • Giữ một danh sách riêng liên kết thông tin nhật ký với các hằng số tương ứng của chúng. Với một hằng số duy nhất, nó sẽ phát triển nhanh.

  • Tìm cách để thực thi một định dạng duy nhất trong nhóm của bạn. Ví dụ, nếu thay vì T2SSQ, ai đó sẽ quyết định viết ST2SQL?


+1, đối với logcuộc gọi sạch , nhưng bạn có thể giải thích cho tôi nhiều hơn không hiểu Log(2, SqlInstal, Action, CopyMapFailure, sOSdrive), ý bạn muốn nói SqlInstalsẽ là biến được xác định của tôi như thế SqlInstal:=[POSTGRESQL INSTALLATION] nào?
PresleyDias

@PresleyDias: SqlInstalcó thể là bất cứ thứ gì, ví dụ như một giá trị 3. Sau đó, Log()giá trị này sẽ được dịch thành hiệu quả [POSTGRESQL INSTALLATION]trước khi được nối với các phần khác của thông điệp tường trình.
Arseni Mourzenko

single format in your teamlà một lựa chọn tốt / tuyệt vời
PresleyDias

3

Hãy thử trích xuất một loạt các chức năng nhỏ để xử lý tất cả các công cụ tìm kiếm lộn xộn. Có rất nhiều mã lặp đi lặp lại có thể rất dễ dàng được thực hiện ở một nơi duy nhất. Ví dụ:

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;

Mẹo nhỏ là xem xét bất kỳ sự trùng lặp nào trong mã của bạn và tìm cách loại bỏ nó. Sử dụng nhiều khoảng trắng và sử dụng đầu / cuối để lợi thế của bạn (nhiều khoảng trắng hơn và dễ dàng tìm / gấp các khối mã). Nó thực sự không nên quá khó khăn. Những phương thức này có thể là một phần của logger của bạn ... thực sự tùy thuộc vào bạn. Nhưng đó có vẻ là một nơi tốt để bắt đầu.


+1, khoảng trắng là cách tốt đẹp .. success := CopyFile()cảm ơn vì ý tưởng này, điều này sẽ giảm một số dòng mã không cần thiết trong trường hợp của tôi
PresleyDias

@ S.Robins tôi đã đọc đúng mã của bạn chưa? phương pháp của bạn được gọi là LogIfFileDoesNotExistbản sao tập tin?
João Portela

1
@ JoãoPortela Vâng ... nó không đẹp lắm và không tuân theo nguyên tắc trách nhiệm duy nhất. Hãy nhớ rằng đây là lần đầu tiên tái cấu trúc khỏi đỉnh đầu của tôi và nhằm mục đích giúp OP thỏa mãn mục tiêu của mình để giảm bớt một số lộn xộn trong mã của mình. Đó có thể là một lựa chọn kém về tên cho phương pháp ở nơi đầu tiên. Tôi sẽ điều chỉnh nó một chút để cải thiện. :)
S.Robins

thật tốt khi thấy bạn đã dành thời gian để giải quyết vấn đề đó, +1.
João Portela

2

Tôi muốn nói rằng ý tưởng đằng sau tùy chọn 2 là tốt nhất. Tuy nhiên, tôi nghĩ rằng hướng đi của bạn làm cho mọi thứ tồi tệ hơn. Số nguyên không có nghĩa gì cả. Khi bạn đang xem mã, bạn sẽ thấy một cái gì đó đang được ghi lại, nhưng bạn không biết cái gì.

Thay vào đó tôi sẽ làm một cái gì đó như thế này:

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

Điều này giữ lại cấu trúc thông báo nhưng cho phép mã của bạn linh hoạt. Bạn có thể xác định các chuỗi không đổi khi cần thiết cho các pha và chỉ sử dụng các chuỗi làm tham số pha. Điều này cho phép bạn có thể thay đổi văn bản thực tế ở một nơi và thực hiện mọi thứ. Lợi ích khác của hàm trợ giúp là văn bản quan trọng nằm trong mã (giống như nó là một nhận xét), nhưng văn bản chỉ quan trọng đối với tệp nhật ký bị trừu tượng hóa.

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

Đây không phải là điều mà bạn đề cập trong câu hỏi của bạn, nhưng tôi nhận thấy về mã của bạn. Sự thụt lề của bạn không nhất quán. Lần đầu tiên bạn sử dụng beginnó không bị thụt vào, nhưng lần thứ hai là như vậy. Bạn làm một điều tương tự với else. Tôi muốn nói rằng điều này quan trọng hơn nhiều so với các dòng nhật ký. Khi thụt lề không nhất quán, nó làm cho việc quét mã và theo dòng chảy trở nên khó khăn. Rất nhiều dòng nhật ký lặp đi lặp lại rất dễ lọc ra khi quét.


1

Làm thế nào về một cái gì đó dọc theo dòng này:

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

Phương thức NewEntry () sẽ xây dựng dòng văn bản (bao gồm thêm [&] xung quanh các mục thích hợp) và chờ đợi cho đến khi phương thức thành công () hoặc fail () được nối, nối thêm dòng với 'thành công' hoặc 'thất bại', và sau đó xuất dòng vào nhật ký. Bạn cũng có thể thực hiện các phương thức khác, chẳng hạn như thông tin () khi mục nhật ký dành cho mục đích khác ngoài thành công / thất bại.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.