Biến tạm thời so với yêu cầu độ dài dòng


10

Tôi đã đọc Tái cấu trúc của Martin Fowler . Nó nói chung là tuyệt vời nhưng một trong những khuyến nghị của Fowler dường như gây ra một chút rắc rối.

Fowler khuyên bạn nên thay thế các biến tạm thời bằng một truy vấn, vì vậy thay vì điều này:

double getPrice() {
    final int basePrice = _quantity * _itemPrice;
    final double discountFactor;
    if (basePrice > 1000) discountFactor = 0.95;
    else discountFactor = 0.98;
    return basePrice * discountFactor;
}

bạn kéo ra một phương thức trợ giúp:

double basePrice() {
    return _quantity * _itemPrice;
}

double getPrice() {
    final double discountFactor;
    if (basePrice() > 1000) discountFactor = 0.95;
    else discountFactor = 0.98;
    return basePrice() * discountFactor;
}

Nói chung, tôi đồng ý ngoại trừ một lý do tôi sử dụng các biến tạm thời là khi một dòng quá dài. Ví dụ:

$host = 'https://api.twilio.com';
$uri = "$host/2010-04-01/Accounts/$accountSid/Usage/Records/AllTime";
$response = Api::makeRequest($uri);

Nếu tôi cố gắng nội tuyến, dòng sẽ dài hơn 80 ký tự.

Thay phiên, tôi kết thúc với chuỗi mã, bản thân chúng không dễ đọc hơn nhiều:

$params = MustacheOptions::build(self::flattenParams($bagcheck->getParams()));

Một số chiến lược để hòa giải hai là gì?


10
80 ký tự là khoảng 1/3 của 1 màn hình của tôi. Bạn có chắc chắn rằng việc gắn bó với 80 dòng char vẫn còn giá trị với bạn?
jk.


Của bạn $host$uriví dụ là loại giả tạo, mặc dù - trừ khi chủ nhà đã được đọc từ một khung cảnh hoặc đầu vào khác, tôi muốn họ là trên cùng một dòng, ngay cả khi nó quấn hoặc đi ra khỏi mép.
Izkata

5
Không cần phải quá giáo điều. Cuốn sách là một danh sách các kỹ thuật có thể được sử dụng khi chúng giúp đỡ, không phải là một bộ quy tắc bạn phải áp dụng ở mọi nơi, mọi lúc. Vấn đề là làm cho mã của bạn dễ bảo trì hơn và dễ đọc hơn. Nếu một công cụ tái cấu trúc không làm điều này, bạn không sử dụng nó.
Sean McS Something 24/1/13

Trong khi tôi nghĩ giới hạn 80 ký tự là hơi quá mức, thì giới hạn tương tự (100?) Là hợp lý. Ví dụ, tôi thường thích lập trình trên màn hình định hướng dọc, do đó, các dòng dài thêm có thể gây khó chịu (ít nhất là nếu chúng là phổ biến).
Thomas Eding

Câu trả lời:


16

Cách thực hiện
1. Hạn chế độ dài dòng tồn tại để bạn có thể xem + hiểu thêm mã. Họ vẫn còn hiệu lực.
2. Nhấn mạnh phán đoán về quy ước mù .
3. Tránh các biến tạm thời trừ khi tối ưu hóa cho hiệu suất .
4. Tránh sử dụng thụt sâu để căn chỉnh trong các câu lệnh nhiều dòng.
5. Chia các câu lệnh dài thành nhiều dòng dọc theo ranh giới ý tưởng :

// prefer this
var distance = Math.Sqrt(
    Math.Pow(point2.GetX() - point1.GetX(), 2) + // x's
    Math.Pow(point2.GetY() - point1.GetY(), 2)   // y's
);

// over this
var distance = Math.Sqrt(Math.Pow(point2.GetX() -
    point1.GetX(), 2) + Math.Pow(point2.GetY() -
    point1.GetY(), 2)); // not even sure if I typed that correctly.

Lý do
Nguồn chính của các vấn đề (gỡ lỗi) của tôi với các biến tạm thời là chúng có xu hướng có thể thay đổi. Cụ thể, tôi sẽ cho rằng chúng là một giá trị khi tôi viết mã, nhưng nếu hàm phức tạp, một số đoạn mã khác sẽ thay đổi trạng thái giữa chừng. (Hoặc ngược lại, trong đó trạng thái của biến vẫn như cũ nhưng kết quả của truy vấn đã thay đổi).

Cân nhắc gắn bó với các truy vấn trừ khi bạn đang tối ưu hóa hiệu suất . Điều này giữ bất kỳ logic nào bạn đã sử dụng để tính giá trị đó ở một nơi duy nhất.

Các ví dụ bạn đã đưa ra (Java và ... PHP?) Đều cho phép các câu lệnh nhiều dòng. Nếu các dòng nhận được dài, phá vỡ chúng. Nguồn jquery đưa điều này đến cực đoan. (Câu lệnh đầu tiên chạy đến dòng 69!) Không phải tôi nhất thiết phải đồng ý, nhưng có nhiều cách khác để làm cho mã của bạn dễ đọc hơn là sử dụng các temp tạm thời.

Một số ví dụ
1. Hướng dẫn kiểu PEP 8 cho trăn (không phải là ví dụ đẹp nhất)
2. Paul M Jones trên Hướng dẫn kiểu Pear (giữa đối số đường)
3. Chiều dài dòng của Oracle + quy ước gói (các tầng hữu ích để giữ đến 80 ký tự)
4. Thực tiễn Java MDN (nhấn mạnh đánh giá của lập trình viên về quy ước)


1
Một phần khác của vấn đề là một biến tạm thời thường tồn tại lâu hơn giá trị của nó. Không phải là một vấn đề trong các khối phạm vi nhỏ, nhưng trong những khối lớn hơn, yeah, một vấn đề lớn.
Ross Patterson

8
Nếu bạn lo lắng về việc tạm thời bị sửa đổi, hãy đặt một const trên nó.
Thomas Eding

3

Tôi nghĩ, lập luận tốt nhất cho việc sử dụng các phương thức của trình trợ giúp thay vì các biến tạm thời là khả năng đọc của con người. Nếu bạn, như một con người, gặp khó khăn hơn khi đọc chuỗi phương thức trợ giúp hơn phương sai tạm thời, tôi không thấy lý do tại sao bạn nên trích xuất chúng.

(hãy sửa lại cho tôi nếu tôi sai)


3

Tôi không nghĩ rằng bạn cần tuân thủ nghiêm ngặt 80 nguyên tắc ký tự hoặc nên rút ra biến tạm thời cục bộ. Nhưng các dòng dài và temps địa phương nên được điều tra để có cách thể hiện tốt hơn cùng một ý tưởng. Về cơ bản, chúng chỉ ra rằng một hàm hoặc dòng nhất định quá phức tạp và chúng ta cần chia nhỏ nó. Nhưng chúng ta cần phải cẩn thận, bởi vì phá vỡ một nhiệm vụ thành nhiều phần theo cách xấu chỉ làm cho tình hình trở nên phức tạp hơn. Vì vậy, tôi sẽ chia mọi thứ thành các thành phần đơn giản và đáng tin cậy.

Hãy để tôi nhìn vào các ví dụ bạn đăng.

$host = 'https://api.twilio.com';
$uri = "$host/2010-04-01/Accounts/$accountSid/Usage/Records/AllTime";
$response = Api::makeRequest($uri);

Quan sát của tôi là tất cả các cuộc gọi api twilio sẽ bắt đầu bằng "https://api.twilio.com/2010-04-1/", và do đó, có một chức năng tái sử dụng rất rõ ràng phải có:

$uri = twilioURL("Accounts/$accountSid/Usage/Records/AllTime")

Trên thực tế, tôi cho rằng lý do duy nhất để tạo URL là thực hiện yêu cầu, vì vậy tôi sẽ làm:

$response = TwilioApi::makeRequest("Accounts/$accountSid/Usage/Records/AllTime")

Trên thực tế, nhiều url thực sự bắt đầu bằng "Tài khoản / $ accountSid", vì vậy tôi cũng có thể trích xuất điều đó:

$response = TwilioApi::makeAccountRequest($accountSid, "Usage/Records/AllTime")

Và nếu chúng ta biến twilio api thành một đối tượng chứa số tài khoản, chúng ta có thể làm một cái gì đó như:

$response = $twilio->makeAccountRequest("Usage/Records/AllTime")

Sử dụng một đối tượng $ twilio có lợi ích là làm cho thử nghiệm đơn vị dễ dàng hơn. Tôi có thể cung cấp cho đối tượng một đối tượng $ twilio khác mà thực tế không gọi lại cho twilio, việc này sẽ nhanh hơn và sẽ không làm những điều kỳ lạ với twilio.

Hãy nhìn vào cái khác

$params = MustacheOptions::build(self::flattenParams($bagcheck->getParams()));

Ở đây tôi nghĩ về một trong hai:

$params = MustacheOptions::buildFromParams($bagcheck->getParams());

hoặc là

$params = MustacheOptions::build($bagcheck->getFlatParams());

hoặc là

$params = MustacheOptions::build(flatParams($backCheck));

Tùy thuộc vào đó là thành ngữ tái sử dụng nhiều hơn.


1

Trên thực tế, tôi không đồng ý với ông Fowler nổi tiếng về vấn đề này trong trường hợp chung.

Ưu điểm của việc trích xuất một phương thức từ mã được in trước đó là tái sử dụng mã; mã trong phương thức hiện đã bị cắt đứt từ cách sử dụng ban đầu và hiện có thể được sử dụng ở những nơi khác trong mã mà không bị sao chép và dán (điều này bắt buộc phải thay đổi ở nhiều nơi nếu logic chung của mã được sao chép phải thay đổi) .

Tuy nhiên, bằng nhau, thường giá trị khái niệm lớn hơn là "tái sử dụng giá trị". Ông Fowler gọi các phương thức trích xuất này để thay thế các biến tạm thời là "truy vấn". Vâng, những gì hiệu quả hơn; truy vấn cơ sở dữ liệu mỗi lần nhiều lần bạn cần một giá trị cụ thể hoặc truy vấn một lần và lưu trữ kết quả (giả sử rằng giá trị đó đủ tĩnh để bạn không muốn nó thay đổi)?

Đối với hầu hết mọi phép tính ngoài phép tính tương đối tầm thường trong ví dụ của bạn, trong hầu hết các ngôn ngữ, việc lưu trữ kết quả của một phép tính rẻ hơn so với việc tiếp tục tính toán. Do đó, khuyến nghị chung để tính toán lại theo yêu cầu là không rõ ràng; nó tốn nhiều thời gian của nhà phát triển hơn và nhiều thời gian CPU hơn và tiết kiệm một lượng bộ nhớ nhỏ, trong hầu hết các hệ thống hiện đại là tài nguyên rẻ nhất trong ba hệ thống đó.

Bây giờ, phương thức của trình trợ giúp, kết hợp với mã khác, có thể được thực hiện "lười biếng". Khi chạy lần đầu tiên, nó sẽ khởi tạo một biến. Tất cả các cuộc gọi tiếp theo sẽ trả về biến đó cho đến khi phương thức được nói rõ ràng để tính toán lại. Đây có thể là một tham số cho phương thức hoặc một cờ được đặt bởi mã khác thay đổi bất kỳ giá trị nào tính toán của phương thức này phụ thuộc vào:

double? _basePrice; //not sure if Java has C#'s "nullable" concept
double basePrice(bool forceCalc)
{
   if(forceCalc || !_basePrice.HasValue)
      return _basePrice = _quantity * _itemPrice;
   return _basePrice.Value;
}

Bây giờ, đối với phép tính tầm thường này, công việc thậm chí còn được thực hiện nhiều hơn so với lưu và do đó, tôi thường khuyên bạn nên gắn bó với biến tạm thời; tuy nhiên, đối với các phép tính phức tạp hơn mà bạn thường muốn tránh chạy nhiều lần và bạn cần ở nhiều nơi trong mã, đây là cách bạn thực hiện.


1

Các phương thức của trình trợ giúp có một vị trí, nhưng bạn phải cẩn thận về việc đảm bảo tính thống nhất của dữ liệu và tăng phạm vi của các biến không cần thiết.

Ví dụ, ví dụ của riêng bạn trích dẫn:

double getPrice() {
    final double discountFactor;
    if (basePrice() > 1000) discountFactor = 0.95;      <--- first call
    else discountFactor = 0.98;
    return basePrice() * discountFactor;                <--- second call
}

Rõ ràng cả hai _quantity_itemPricelà các biến toàn cục (hoặc ít nhất là cấp độ lớp) và do đó, có khả năng chúng được sửa đổi bên ngoàigetPrice()

Do đó, có khả năng cuộc gọi đầu tiên basePrice()trả về giá trị khác với cuộc gọi thứ hai!

Do đó, tôi muốn đề xuất rằng các hàm trợ giúp có thể hữu ích để cô lập toán học phức tạp, nhưng để thay thế cho các biến cục bộ, bạn cần cẩn thận.


Bạn cũng phải tránh reductio ad absurdum - có nên tính toán discountFactorphương pháp này không? Vì vậy, ví dụ của bạn trở thành:

double getPrice()
{
    final double basePrice      = calculateBasePrice();
    final double discountFactor = calculateDiscount( basePrice );

    return basePrice * discountFactor;
}

Phân vùng vượt quá một mức nhất định thực sự làm cho mã ít đọc hơn.


+1 để làm cho mã ít đọc hơn. Phân vùng quá mức có thể che giấu vấn đề kinh doanh mà mã nguồn đang cố gắng giải quyết. Có thể có những trường hợp đặc biệt khi một phiếu giảm giá được áp dụng trong getprice (), nhưng nếu điều đó được ẩn sâu trong một chuỗi các lệnh gọi chức năng thì quy tắc kinh doanh cũng bị ẩn đi.
Phản ứng

0

Nếu bạn tình cờ làm việc trong một ngôn ngữ có các tham số được đặt tên (ObjectiveC, Python, Ruby, v.v.), các temp tạm thời sẽ ít hữu ích hơn.

Tuy nhiên, trong ví dụ Baseprice của bạn, truy vấn có thể mất một chút thời gian để thực thi và bạn có thể muốn lưu trữ kết quả trong một biến tạm thời để sử dụng trong tương lai.

Tuy nhiên, giống như bạn, tôi sử dụng các biến tạm thời để xem xét rõ ràng và độ dài dòng.

Tôi cũng đã thấy các lập trình viên làm như sau trong PHP. Nó thú vị và tuyệt vời để gỡ lỗi, nhưng nó hơi lạ.

$rs = DB::query( $query = "SELECT * FROM table" );
if (DEBUG) echo $query;
// do something with $rs

0

Lý do đằng sau khuyến nghị này là bạn muốn có thể sử dụng cùng một tiền mã hóa ở nơi khác trong ứng dụng của mình. Xem Thay thế Temp bằng Truy vấn trong danh mục mẫu tái cấu trúc:

Phương thức mới sau đó có thể được sử dụng trong các phương thức khác

    double basePrice = _quantity * _itemPrice;
    if (basePrice > 1000)
        return basePrice * 0.95;
    else
        return basePrice * 0.98;

           http://i.stack.imgur.com/mKbQM.gif

    if (basePrice() > 1000)
        return basePrice() * 0.95;
    else
        return basePrice() * 0.98;
...
double basePrice() {
    return _quantity * _itemPrice;
}

Do đó, trong ví dụ về máy chủ và URI của bạn, tôi sẽ chỉ áp dụng đề xuất này nếu tôi dự định sử dụng lại cùng một định nghĩa URI hoặc máy chủ lưu trữ.

Nếu đây là trường hợp, do không gian tên, tôi sẽ không định nghĩa phương thức uri () hoặc host () toàn cầu, nhưng một tên có nhiều thông tin hơn, như twilio_host () hoặc archive_Vquest_uri ().

Sau đó, đối với vấn đề độ dài dòng, tôi thấy một số tùy chọn:

  • Tạo một biến cục bộ, như uri = archive_request_uri().

Đặt vấn đề: Trong phương thức hiện tại, bạn muốn URI là cái được đề cập. Định nghĩa URI vẫn là yếu tố.

  • Xác định một phương thức cục bộ, như uri() { return archive_request_uri() }

Nếu bạn thường sử dụng đề xuất của Fowler, bạn sẽ biết rằng phương thức uri () là cùng một mẫu.

Nếu do lựa chọn ngôn ngữ, bạn cần truy cập phương thức cục bộ bằng 'tự.', Tôi khuyên bạn nên sử dụng giải pháp đầu tiên, để tăng tính biểu cảm (trong Python, tôi sẽ xác định hàm uri bên trong phương thức hiện tạ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.