Làm thế nào để tôi biết làm thế nào có thể tái sử dụng phương pháp của tôi? [đóng cửa]


133

Tôi đang bận tâm về việc kinh doanh của mình ở nhà và vợ tôi đến gặp tôi và nói

Anh yêu .. Bạn có thể in tất cả các Tiết kiệm ánh sáng ban ngày trên toàn thế giới cho năm 2018 trong bảng điều khiển không? Tôi cần kiểm tra một cái gì đó.

Và tôi vô cùng hạnh phúc vì đó là điều tôi đã chờ đợi cả đời với trải nghiệm Java của mình và nghĩ ra:

import java.time.*;
import java.util.Set;

class App {
    void dayLightSavings() {
        final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(
            zoneId -> {
                LocalDateTime dateTime = LocalDateTime.of(
                    LocalDate.of(2018, 1, 1), 
                    LocalTime.of(0, 0, 0)
                );
                ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
                while (2018 == now.getYear()) {
                    int hour = now.getHour();
                    now = now.plusHours(1);
                    if (now.getHour() == hour) {
                        System.out.println(now);
                    }
                }
            }
        );
    }
}

Nhưng sau đó cô ấy nói rằng cô ấy chỉ kiểm tra tôi xem tôi có phải là kỹ sư phần mềm được đào tạo về đạo đức hay không, và nói với tôi rằng có vẻ như tôi không phải là người (lấy từ đây ) ..

Cần lưu ý rằng không có kỹ sư phần mềm được đào tạo đạo đức nào có thể đồng ý viết một quy trình DestBaghdad. Thay vào đó, đạo đức nghề nghiệp cơ bản sẽ yêu cầu anh ta viết một thủ tục DestCity, trong đó Baghdad có thể được đưa ra như một tham số.

Và tôi thích, tốt thôi, ok, bạn đã cho tôi .. Vượt qua bất kỳ năm nào bạn thích, ở đây bạn đi:

import java.time.*;
import java.util.Set;

class App {
    void dayLightSavings(int year) {
        final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(
            zoneId -> {
                LocalDateTime dateTime = LocalDateTime.of(
                    LocalDate.of(year, 1, 1), 
                    LocalTime.of(0, 0, 0)
                );
                ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
                while (year == now.getYear()) {
                    // rest is same..

Nhưng làm thế nào để tôi biết bao nhiêu (và cái gì) để tham số hóa? Rốt cuộc, cô ấy có thể nói ..

  • cô ấy muốn vượt qua một trình định dạng chuỗi tùy chỉnh, có thể cô ấy không thích định dạng tôi đã in: 2018-10-28T02:00+01:00[Arctic/Longyearbyen]

void dayLightSavings(int year, DateTimeFormatter dtf)

  • cô ấy chỉ quan tâm đến những khoảng thời gian nhất định trong tháng

void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd)

  • cô ấy quan tâm đến những khoảng thời gian nhất định

void dayLightSavings(int year, DateTimeFormatter dtf, int monthStart, int monthEnd, int hourStart, int hourend)

Nếu bạn đang tìm kiếm một câu hỏi cụ thể:

Nếu destroyCity(City city)tốt hơn destroyBaghdad(), takeActionOnCity(Action action, City city)thậm chí còn tốt hơn? Tại sao tại sao không?

Sau khi tất cả, lần đầu tiên tôi có thể gọi nó với Action.DESTROYđó Action.REBUILD, phải không?

Nhưng hành động trên các thành phố là không đủ cho tôi, làm thế nào takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)? Rốt cuộc, tôi không muốn gọi:

takeActionOnCity(Action.DESTORY, City.BAGHDAD);

sau đó

takeActionOnCity(Action.DESTORY, City.ERBIL);

và như vậy khi tôi có thể làm:

takeActionOnGeographicArea(Action.DESTORY, Country.IRAQ);

ps Tôi chỉ xây dựng câu hỏi của mình xung quanh câu trích dẫn mà tôi đã đề cập, tôi không có gì chống lại bất kỳ quốc gia, tôn giáo, chủng tộc hay bất cứ điều gì trên thế giới. Tôi chỉ đang cố gắng để làm cho một điểm.



71
Điểm bạn đang thực hiện ở đây là một điểm tôi đã cố gắng thể hiện nhiều lần: tính tổng quát là đắt đỏ, và do đó phải được chứng minh bằng các lợi ích rõ ràng, cụ thể . Nhưng nó còn đi sâu hơn vậy; ngôn ngữ lập trình được tạo ra bởi các nhà thiết kế của họ để làm cho một số loại tổng quát dễ dàng hơn các ngôn ngữ khác và điều đó ảnh hưởng đến sự lựa chọn của chúng tôi là nhà phát triển. Thật dễ dàng để tham số hóa một phương thức theo một giá trị và khi đó là công cụ đơn giản nhất bạn có trong hộp công cụ của mình, sự cám dỗ là sử dụng nó bất kể nó có hợp lý với người dùng hay không.
Eric Lippert

30
Tái sử dụng không phải là thứ bạn muốn vì mục đích riêng của nó. Chúng tôi ưu tiên sử dụng lại vì chúng tôi tin rằng các tạo phẩm mã rất tốn kém để xây dựng và do đó nên có thể sử dụng trong càng nhiều kịch bản càng tốt, để khấu hao chi phí đó qua các kịch bản đó. Niềm tin này thường không được chứng minh bằng các quan sát, và lời khuyên để thiết kế cho khả năng sử dụng lại do đó thường xuyên bị áp dụng sai . Thiết kế mã của bạn để giảm tổng chi phí của ứng dụng .
Eric Lippert

7
Vợ bạn là người vô đạo đức vì đã lãng phí thời gian của bạn bằng cách nói dối bạn. Cô yêu cầu một câu trả lời, và đưa ra một phương tiện được đề xuất; Theo hợp đồng đó, làm thế nào bạn có được đầu ra đó chỉ là giữa bạn và chính bạn. Ngoài ra, destroyCity(target)là cách phi đạo đức hơn destroyBagdad()! Loại quái vật nào viết một chương trình quét sạch một thành phố, huống chi là bất kỳ thành phố nào trên thế giới? Nếu hệ thống bị xâm phạm thì sao?! Ngoài ra, quản lý thời gian / tài nguyên (nỗ lực đầu tư) có liên quan gì đến đạo đức? Miễn là hợp đồng bằng lời nói / bằng văn bản đã được hoàn thành theo thỏa thuận.
Tezra

25
Tôi nghĩ rằng bạn có thể đang đọc quá nhiều vào trò đùa này. Đó là một trò đùa về cách các lập trình viên máy tính đưa ra các quyết định đạo đức tồi tệ, bởi vì họ ưu tiên các cân nhắc kỹ thuật hơn các tác động của công việc của họ đối với con người. Nó không có ý định là lời khuyên tốt về thiết kế chương trình.
Eric Lippert

Câu trả lời:


114

Đó là tất cả các con rùa xuống.

Hoặc trừu tượng trong trường hợp này.

Mã hóa thực hành tốt là thứ có thể được áp dụng vô hạn, và đến một lúc nào đó bạn đang trừu tượng hóa vì mục đích trừu tượng hóa, điều đó có nghĩa là bạn đã đưa nó đi quá xa. Tìm ra dòng đó không phải là điều dễ dàng để đưa vào quy tắc, vì nó phụ thuộc rất nhiều vào môi trường của bạn.

Ví dụ: chúng tôi đã có những khách hàng được biết là yêu cầu các ứng dụng đơn giản trước nhưng sau đó yêu cầu mở rộng. Chúng tôi cũng đã có những khách hàng hỏi họ muốn gì và thường không bao giờ quay lại với chúng tôi để mở rộng.
Cách tiếp cận của bạn sẽ thay đổi theo từng khách hàng. Đối với các khách hàng đầu tiên, nó sẽ trả tiền để trước emptively trừu tượng mã vì bạn một cách hợp lý chắc chắn rằng bạn sẽ cần phải xem lại đoạn mã này trong tương lai. Đối với khách hàng thứ hai, bạn có thể không muốn đầu tư thêm nỗ lực đó nếu bạn mong muốn họ không muốn mở rộng ứng dụng vào bất kỳ thời điểm nào (lưu ý: điều này không có nghĩa là bạn không tuân theo bất kỳ thực tiễn tốt nào, mà chỉ đơn giản là rằng bạn tránh làm nhiều hơn hiện đang cần thiết.

Làm thế nào để tôi biết các tính năng để thực hiện?

Lý do tôi đề cập ở trên là vì bạn đã rơi vào cái bẫy này:

Nhưng làm thế nào để tôi biết bao nhiêu (và cái gì) để tham số hóa? Rốt cuộc, cô ấy có thể nói .

"Cô ấy có thể nói" không phải là một yêu cầu kinh doanh hiện tại. Đó là một dự đoán về một yêu cầu kinh doanh trong tương lai. Theo nguyên tắc chung, đừng dựa vào dự đoán, chỉ phát triển những gì hiện đang được yêu cầu.

Tuy nhiên, bối cảnh áp dụng ở đây. Tôi không biết vợ bạn. Có lẽ bạn đánh giá chính xác rằng cô ấy thực tế sẽ muốn điều này. Nhưng bạn vẫn nên xác nhận với khách hàng rằng đây thực sự là những gì họ muốn, bởi vì nếu không, bạn sẽ dành thời gian để phát triển một tính năng mà bạn sẽ không bao giờ kết thúc sử dụng.

Làm thế nào để tôi biết kiến ​​trúc nào sẽ thực hiện?

Điều này là khó khăn hơn. Khách hàng không quan tâm đến mã nội bộ, vì vậy bạn không thể hỏi họ nếu họ cần. Ý kiến ​​của họ về vấn đề này hầu hết không liên quan.

Tuy nhiên, bạn vẫn có thể xác nhận sự cần thiết của việc đó bằng cách đặt câu hỏi đúng cho khách hàng. Thay vì hỏi về kiến ​​trúc, hãy hỏi họ về những kỳ vọng của họ về sự phát triển hoặc mở rộng trong tương lai cho cơ sở mã. Bạn cũng có thể hỏi liệu mục tiêu hiện tại có thời hạn không, bởi vì bạn có thể không thực hiện được kiến ​​trúc ưa thích của mình trong khung thời gian cần thiết.

Làm thế nào để tôi biết khi nào trừu tượng hóa mã của tôi hơn nữa?

Tôi không biết tôi đã đọc nó ở đâu (nếu có ai biết, hãy cho tôi biết và tôi sẽ cung cấp tín dụng), nhưng một nguyên tắc nhỏ là các nhà phát triển nên tính như một người thượng cổ: một, hai nhiều .

nhập mô tả hình ảnh ở đây XKCD # 764

Nói cách khác, khi một thuật toán / mẫu nhất định đang được sử dụng lần thứ ba , nó nên được trừu tượng hóa để nó có thể được sử dụng lại (= có thể sử dụng nhiều lần).

Nói rõ hơn, tôi không ngụ ý rằng bạn không nên viết mã có thể sử dụng lại khi chỉ có hai trường hợp thuật toán được sử dụng. Tất nhiên bạn cũng có thể trừu tượng hóa điều đó, nhưng quy tắc nên là trong ba trường hợp bạn phải trừu tượng hóa.

Một lần nữa, yếu tố này trong sự mong đợi của bạn. Nếu bạn đã biết rằng bạn cần ba hoặc nhiều trường hợp, tất nhiên bạn có thể ngay lập tức trừu tượng. Nhưng nếu bạn chỉ đoán rằng bạn có thể muốn thực hiện nó nhiều lần hơn, thì tính chính xác của việc thực hiện trừu tượng hoàn toàn phụ thuộc vào tính chính xác của dự đoán của bạn.
Nếu bạn đoán đúng, bạn đã tiết kiệm cho mình một chút thời gian. Nếu bạn đoán sai, bạn đã lãng phí một phần thời gian và công sức của mình và có thể làm tổn hại đến kiến ​​trúc của bạn để thực hiện một cái gì đó mà bạn không cần.

Nếu destroyCity(City city)tốt hơn destroyBaghdad(), takeActionOnCity(Action action, City city)thậm chí còn tốt hơn? Tại sao tại sao không?

Điều đó phụ thuộc rất nhiều vào nhiều thứ:

  • Có nhiều hành động có thể được thực hiện trên bất kỳ thành phố?
  • Những hành động này có thể được sử dụng thay thế cho nhau? Bởi vì nếu các hành động "phá hủy" và "xây dựng lại" có các cách thực hiện hoàn toàn khác nhau, thì sẽ không có điểm nào trong việc hợp nhất hai takeActionOnCityphương thức trong một phương thức duy nhất .

Ngoài ra, hãy lưu ý rằng nếu bạn tóm tắt đệ quy này, bạn sẽ kết thúc bằng một phương thức trừu tượng đến mức không có gì khác hơn là một container để chạy phương thức khác, điều đó có nghĩa là bạn đã làm cho phương thức của mình không liên quan và vô nghĩa.
Nếu toàn bộ takeActionOnCity(Action action, City city)phần thân phương thức của bạn kết thúc không có gì hơn action.TakeOn(city);, bạn nên tự hỏi liệu takeActionOnCityphương thức đó có thực sự có mục đích hay không chỉ là một lớp bổ sung không có giá trị gì.

Nhưng hành động trên các thành phố là không đủ cho tôi, làm thế nào takeActionOnGeographicArea(Action action, GeographicalArea GeographicalArea)?

Câu hỏi tương tự xuất hiện ở đây:

  • Bạn có một trường hợp sử dụng cho các khu vực địa lý?
  • Là việc thực hiện một hành động trên một thành phố và một khu vực giống nhau?
  • Bất kỳ hành động có thể được thực hiện trên bất kỳ khu vực / thành phố?

Nếu bạn có thể trả lời dứt khoát "có" cho cả ba, thì một sự trừu tượng được bảo đảm.


16
Tôi không thể nhấn mạnh quy tắc "một, hai, nhiều". Có những khả năng vô hạn để trừu tượng / tham số hóa một cái gì đó, nhưng tập hợp con hữu ích là nhỏ, thường bằng không. Biết chính xác biến thể nào có giá trị thường chỉ có thể được xác định khi nhìn lại. Vì vậy, hãy tuân thủ các yêu cầu trước mắt * và thêm độ phức tạp khi cần bởi các yêu cầu mới hoặc nhận thức muộn. * Đôi khi bạn biết rõ không gian vấn đề, sau đó có thể thêm vào một cái gì đó bởi vì bạn biết bạn cần nó vào ngày mai. Nhưng sử dụng sức mạnh này một cách khôn ngoan, nó cũng có thể dẫn đến hủy hoại.
Christian Sauer

2
> "Tôi không biết tôi đã đọc nó ở đâu [..]". Bạn có thể đã đọc Mã hóa kinh dị: Quy tắc của ba .
Rune

10
"Một, hai, nhiều quy tắc" thực sự ở đó để ngăn bạn xây dựng sự trừu tượng sai bằng cách áp dụng DRY một cách mù quáng. Vấn đề là, hai đoạn mã có thể bắt đầu trông gần giống nhau, vì vậy thật hấp dẫn để trừu tượng hóa sự khác biệt; nhưng ngay từ đầu, bạn không biết phần nào của mã ổn định và phần nào không; bên cạnh đó, nó có thể chỉ ra rằng họ thực sự cần phải phát triển độc lập (các kiểu thay đổi khác nhau, các nhóm trách nhiệm khác nhau). Trong cả hai trường hợp, một sự trừu tượng sai làm việc chống lại bạn và cản trở bạn.
Filip Milovanović

4
Chờ đợi hơn hai ví dụ về "cùng một logic" cho phép bạn trở thành một người đánh giá tốt hơn về những gì nên được trừu tượng hóa, và làm thế nào (và thực sự, đó là về việc quản lý các phụ thuộc / khớp nối giữa các mã với các mẫu thay đổi khác nhau).
Filip Milovanović

1
@kukis: Dòng thực tế nên được rút ra ở mức 2 (theo nhận xét của Baldrickk): zero-one-many (như trường hợp quan hệ cơ sở dữ liệu). Tuy nhiên, điều này mở ra cánh cửa cho hành vi tìm kiếm mô hình không cần thiết. Hai thứ có thể trông giống nhau một cách mơ hồ nhưng điều đó không có nghĩa là chúng thực sự giống nhau. Tuy nhiên, khi một thể hiện thứ ba bước vào cuộc cạnh tranh giống với cả thể hiện thứ nhất và trường hợp thứ hai, bạn có thể đưa ra một phán đoán chính xác hơn rằng sự giống nhau của chúng thực sự là một mô hình có thể tái sử dụng. Vì vậy, dòng cảm giác chung được vẽ ở 3, yếu tố gây ra lỗi của con người khi phát hiện ra "mẫu" giữa chỉ hai trường hợp.
Flater

44

Thực hành

Đây là Software Engineering SE, nhưng phần mềm chế tạo là nghệ thuật hơn nhiều so với kỹ thuật. Không có thuật toán phổ quát để theo dõi hoặc đo lường để tìm ra mức độ tái sử dụng là đủ. Giống như với bất cứ điều gì, bạn càng thực hành nhiều chương trình thiết kế, bạn sẽ nhận được nó tốt hơn. Bạn sẽ có cảm giác tốt hơn về những gì "đủ" bởi vì bạn sẽ thấy những gì sai và làm thế nào nó sai khi bạn tham số hóa quá nhiều hoặc quá ít.

Điều đó bây giờ không hữu ích lắm , vậy còn một số hướng dẫn thì sao?

Nhìn lại câu hỏi của bạn. Có rất nhiều "cô ấy có thể nói" và "tôi có thể". Rất nhiều tuyên bố lý thuyết về một số nhu cầu trong tương lai. Con người rất dự đoán về tương lai. Và bạn (rất có thể) là một con người. Vấn đề lớn nhất của thiết kế phần mềm là cố gắng giải thích cho tương lai mà bạn không biết.

Hướng dẫn 1: Bạn không cần nó

Nghiêm túc. Chỉ cần dừng lại. Thường xuyên hơn không, vấn đề trong tương lai tưởng tượng đó không xuất hiện - và chắc chắn nó sẽ không xuất hiện giống như bạn tưởng tượng.

Hướng dẫn 2: Chi phí / Lợi ích

Thật tuyệt, chương trình nhỏ đó khiến bạn mất vài giờ để viết có lẽ? Vậy điều gì sẽ xảy ra nếu vợ bạn quay lại và yêu cầu những điều đó? Trường hợp xấu nhất, bạn dành thêm một vài giờ để cùng nhau thực hiện một chương trình khác để làm điều đó. Trong trường hợp này, không quá nhiều thời gian để làm cho chương trình này linh hoạt hơn. Và nó sẽ không thêm nhiều vào tốc độ thời gian chạy hoặc sử dụng bộ nhớ. Nhưng các chương trình không tầm thường có câu trả lời khác nhau. Các kịch bản khác nhau có câu trả lời khác nhau. Tại một số điểm, các chi phí rõ ràng không có giá trị lợi ích ngay cả với các kỹ năng nói không hoàn hảo trong tương lai.

Hướng dẫn 3: Tập trung vào hằng số

Nhìn lại câu hỏi. Trong mã gốc của bạn, có rất nhiều số nguyên không đổi. 2018, 1. Số nguyên không đổi, chuỗi không đổi ... Chúng là những thứ có khả năng nhất không cần phải không đổi . Tốt hơn nữa, họ chỉ mất một ít thời gian để tham số hóa (hoặc ít nhất là xác định là hằng số thực tế). Nhưng một điều cần cảnh giác là hành vi liên tục . CácSystem.out.printlnví dụ. Đó là loại giả định về việc sử dụng có xu hướng thay đổi trong tương lai và có xu hướng rất tốn kém để khắc phục. Không chỉ vậy, nhưng IO như thế này làm cho hàm không tinh khiết (cùng với việc tìm nạp phần nào múi giờ). Tham số hóa hành vi đó có thể làm cho chức năng tinh khiết hơn dẫn đến tăng tính linh hoạt và khả năng kiểm tra. Lợi ích lớn với chi phí tối thiểu (đặc biệt nếu bạn thực hiện quá tải sử dụng System.outtheo mặc định).


1
Đó chỉ là một hướng dẫn, các số 1 vẫn ổn nhưng bạn hãy nhìn vào chúng và đi, điều này có bao giờ thay đổi không? Và println có thể được tham số hóa với hàm bậc cao hơn - mặc dù Java không tuyệt vời lắm.
Telastyn

5
@KorayTugay: nếu chương trình thực sự dành cho vợ bạn ở nhà, YAGNI sẽ nói với bạn rằng phiên bản ban đầu của bạn là hoàn hảo và bạn không nên đầu tư thêm thời gian để giới thiệu các hằng số hoặc tham số. YAGNI cần bối cảnh - chương trình của bạn là một giải pháp vứt đi, hay chương trình di chuyển chỉ chạy trong vài tháng, hay nó là một phần của hệ thống ERP khổng lồ, dự định sẽ được sử dụng và duy trì trong nhiều thập kỷ?
Doc Brown

8
@KorayTugay: Tách I / O khỏi tính toán là một kỹ thuật cấu trúc chương trình cơ bản. Tạo dữ liệu riêng biệt từ việc lọc dữ liệu khỏi chuyển đổi dữ liệu từ việc tiêu thụ dữ liệu từ việc trình bày dữ liệu. Bạn nên nghiên cứu một số chương trình chức năng, sau đó bạn sẽ thấy điều này rõ ràng hơn. Trong lập trình chức năng, việc tạo ra một lượng dữ liệu vô hạn là khá phổ biến, chỉ lọc ra dữ liệu bạn quan tâm, chuyển đổi dữ liệu thành định dạng bạn cần, xây dựng chuỗi từ đó và in chuỗi này theo 5 chức năng khác nhau, một cho mỗi bước.
Jörg W Mittag

3
Là một sidenote, mạnh mẽ theo YAGNI dẫn đến cần phải liên tục tái cấu trúc: "Được sử dụng mà không tái cấu trúc liên tục, nó có thể dẫn đến mã vô tổ chức và làm lại lớn, được gọi là nợ kỹ thuật." Vì vậy, mặc dù YAGNI là một điều tốt nói chung, nó đi kèm với trách nhiệm lớn là xem xét lại và đánh giá lại mã, đó không phải là điều mà mọi nhà phát triển / công ty sẵn sàng làm.
Flater

4
@Telastyn: Tôi khuyên bạn nên mở rộng câu hỏi thành Điều này sẽ không bao giờ thay đổi và ý định của mã có thể đọc được một cách tầm thường mà không đặt tên hằng số ? Ngay cả đối với các giá trị không bao giờ thay đổi, có thể có liên quan để đặt tên cho chúng chỉ để giữ cho mọi thứ có thể đọc được.
Flater

27

Thứ nhất: Không nhà phát triển phần mềm có đầu óc bảo mật nào sẽ viết phương thức DestCity mà không thông qua Mã thông báo ủy quyền vì bất kỳ lý do nào.

Tôi cũng có thể viết bất cứ điều gì như một mệnh lệnh có trí tuệ hiển nhiên mà không áp dụng nó trong bối cảnh khác. Tại sao cần phải ủy quyền cho một chuỗi nối?

Thứ hai: Tất cả các mã khi được thực thi phải được chỉ định đầy đủ .

Không có vấn đề gì cho dù quyết định được mã hóa cứng tại chỗ, hay hoãn lại sang lớp khác. Tại một số điểm, có một đoạn mã trong một số ngôn ngữ biết cả những gì sẽ bị phá hủy và làm thế nào để hướng dẫn nó.

Đó có thể là trong cùng một tệp đối tượng destroyCity(xyz)và nó có thể nằm trong tệp cấu hình: destroy {"city": "XYZ"}"hoặc có thể là một loạt các lần nhấp và nhấn phím trong giao diện người dùng.

Thứ ba:

Anh yêu .. Bạn có thể in tất cả các Tiết kiệm ánh sáng ban ngày trên toàn thế giới cho năm 2018 trong bảng điều khiển không? Tôi cần kiểm tra một cái gì đó.

là một tập hợp các yêu cầu rất khác nhau để:

cô ấy muốn vượt qua một trình định dạng chuỗi tùy chỉnh, ... chỉ quan tâm đến các khoảng thời gian nhất định trong tháng, ... [và] quan tâm đến các khoảng thời gian nhất định ...

Bây giờ bộ yêu cầu thứ hai rõ ràng làm cho một công cụ linh hoạt hơn. Nó có đối tượng mục tiêu rộng hơn và lĩnh vực ứng dụng rộng hơn. Điều nguy hiểm ở đây là ứng dụng linh hoạt nhất trên thế giới trên thực tế là một trình biên dịch cho mã máy. Đây thực sự là một chương trình rất chung chung, nó có thể xây dựng bất cứ thứ gì để tạo ra máy tính bất cứ thứ gì bạn cần (trong giới hạn của phần cứng của nó).

Nói chung, những người cần phần mềm không muốn một cái gì đó chung chung; họ muốn một cái gì đó cụ thể. Bằng cách đưa ra nhiều lựa chọn hơn, bạn thực sự làm cho cuộc sống của họ trở nên phức tạp hơn. Nếu họ muốn sự phức tạp đó, thay vào đó họ sẽ sử dụng trình biên dịch, không hỏi bạn.

Vợ bạn đã yêu cầu chức năng, và không xác định rõ các yêu cầu của cô ấy với bạn. Trong trường hợp này có vẻ như là có chủ đích, và nói chung là vì họ không biết gì hơn. Nếu không, họ sẽ chỉ sử dụng trình biên dịch. Vì vậy, vấn đề đầu tiên là bạn không hỏi thêm chi tiết về chính xác những gì cô ấy muốn làm. Cô ấy muốn chạy cái này trong nhiều năm? Cô ấy muốn nó trong một tệp CSV? Bạn đã không tìm ra quyết định nào cô ấy muốn đưa ra, và những gì cô ấy yêu cầu bạn tìm ra và quyết định cho cô ấy. Khi bạn đã tìm ra quyết định nào cần được hoãn lại, bạn có thể tìm ra cách truyền đạt các quyết định đó thông qua các tham số (và các phương tiện có thể định cấu hình khác).

Điều đó đang được nói, hầu hết các khách hàng bỏ lỡ giao tiếp, giả định hoặc không biết gì về một số chi tiết nhất định (hay còn gọi là quyết định) mà họ thực sự muốn tự đưa ra hoặc họ thực sự không muốn thực hiện (nhưng nghe có vẻ tuyệt vời). Đây là lý do tại sao các phương pháp làm việc như PDSA (kế hoạch phát triển-nghiên cứu-hành động) rất quan trọng. Bạn đã lên kế hoạch cho công việc phù hợp với yêu cầu và sau đó bạn đã phát triển một tập hợp các quyết định (mã). Bây giờ là lúc để nghiên cứu nó, một mình hoặc với khách hàng của bạn và tìm hiểu những điều mới, và những điều này cho thấy suy nghĩ của bạn trong tương lai. Cuối cùng hành động theo những hiểu biết mới của bạn - cập nhật các yêu cầu, tinh chỉnh quy trình, nhận các công cụ mới, v.v ... Sau đó bắt đầu lập kế hoạch lại. Điều này sẽ tiết lộ bất kỳ yêu cầu ẩn theo thời gian, và chứng minh sự tiến bộ cho nhiều khách hàng.

Cuối cùng. Thời gian của bạn rất quan trọng ; nó rất thật và rất hữu hạn Mỗi quyết định bạn đưa ra đòi hỏi nhiều quyết định ẩn khác, và đây là những gì phát triển phần mềm. Trì hoãn một quyết định như là một đối số có thể làm cho hàm hiện tại đơn giản hơn, nhưng nó làm cho một nơi khác phức tạp hơn. Là quyết định có liên quan ở vị trí khác? Có liên quan hơn ở đây? Nó thực sự quyết định của ai? Bạn đang quyết định điều này; đây là mã hóa Nếu bạn lặp lại các bộ quyết định thường xuyên, có một lợi ích rất thực tế trong việc mã hóa chúng trong một số trừu tượng. XKCD có một quan điểm hữu ích ở đây. Và điều này có liên quan ở cấp độ của một hệ thống có thể là chức năng, mô-đun, chương trình, v.v.

Lời khuyên khi bắt đầu ngụ ý rằng các quyết định mà chức năng của bạn không có quyền đưa ra nên được thông qua dưới dạng đối số. Vấn đề là một DestroyBaghdadchức năng thực sự có thể là chức năng có quyền đó.


+1 yêu phần về trình biên dịch!
Lee

4

Có rất nhiều câu trả lời dài dòng ở đây, nhưng thực lòng tôi nghĩ nó siêu đơn giản

Bất kỳ thông tin được mã hóa cứng nào bạn có trong hàm không phải là một phần của tên hàm phải là một tham số.

vậy trong chức năng của bạn

class App {
    void dayLightSavings() {
        final Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
        availableZoneIds.forEach(zoneId -> {
            LocalDateTime dateTime = LocalDateTime.of(LocalDate.of(2018, 1, 1), LocalTime.of(0, 0, 0));
            ZonedDateTime now = ZonedDateTime.of(dateTime, ZoneId.of(zoneId));
            while (2018 == now.getYear()) {
                int hour = now.getHour();
                now = now.plusHours(1);
                if (now.getHour() == hour) {
                    System.out.println(now);
                }
            }
        });
    }
}

Bạn có:

The zoneIds
2018, 1, 1
System.out

Vì vậy, tôi sẽ chuyển tất cả những thứ này sang tham số ở dạng này hay dạng khác. Bạn có thể lập luận rằng các ZoneIds ẩn trong tên hàm, có thể bạn sẽ muốn làm cho nó thậm chí nhiều hơn bằng cách thay đổi nó thành "Ánh sáng ban ngàySroundTheWorld" hoặc một cái gì đó

Bạn không có chuỗi định dạng, vì vậy, thêm một chuỗi là một yêu cầu tính năng và bạn nên giới thiệu vợ mình với ví dụ Jira của gia đình. Nó có thể được đưa vào hồ sơ tồn đọng và ưu tiên tại cuộc họp ủy ban quản lý dự án thích hợp.


1
Bạn (địa chỉ bản thân mình cho OP) chắc chắn không nên thêm một chuỗi định dạng, vì bạn không nên in bất cứ điều gì. Một điều về mã này hoàn toàn ngăn chặn việc sử dụng lại là nó in. Nó sẽ trả về các khu vực, hoặc bản đồ của các khu vực khi chúng tắt DST. (Mặc dù tại sao nó chỉ xác định khi tắt DST chứ không phải trên DST, tôi không hiểu. Nó dường như không khớp với tuyên bố vấn đề.)
David Conrad

yêu cầu là in ra bàn điều khiển. bạn có thể giảm nhẹ khớp nối chặt chẽ bằng cách chuyển vào luồng đầu ra dưới dạng tham số như tôi đề xuất
Ewan

1
Mặc dù vậy, nếu bạn muốn mã có thể được sử dụng lại, bạn không nên in ra bàn điều khiển. Viết một phương thức trả về kết quả, sau đó viết một người gọi nhận được chúng và in ra. Điều đó cũng làm cho nó có thể kiểm tra được. Nếu bạn muốn nó tạo ra đầu ra, tôi sẽ không chuyển vào luồng đầu ra, tôi sẽ chuyển vào Người tiêu dùng.
David Conrad

một luồng thông tin là một người tiêu dùng
Ewan

Không, một OutputStream không phải là Người tiêu dùng .
David Conrad

4

Nói tóm lại, đừng thiết kế phần mềm của bạn để có thể sử dụng lại vì không người dùng cuối quan tâm nếu các chức năng của bạn có thể được sử dụng lại. Thay vào đó, kỹ sư về tính dễ hiểu thiết kế - mã của tôi có dễ cho người khác hoặc bản thân hay quên trong tương lai không? - và thiết kế linh hoạt- khi tôi chắc chắn phải sửa lỗi, thêm tính năng hoặc sửa đổi chức năng, mã của tôi sẽ chống lại các thay đổi bao nhiêu? Điều duy nhất khách hàng của bạn quan tâm là bạn có thể phản hồi nhanh như thế nào khi cô ấy báo cáo lỗi hoặc yêu cầu thay đổi. Đặt những câu hỏi về thiết kế của bạn một cách tình cờ có xu hướng dẫn đến mã có thể tái sử dụng, nhưng cách tiếp cận này giúp bạn tập trung vào việc tránh các vấn đề thực sự bạn sẽ gặp phải trong vòng đời của mã đó để bạn có thể phục vụ người dùng cuối tốt hơn thay vì theo đuổi cao cả, không thực tế "kỹ thuật" lý tưởng để làm hài lòng những người yêu râu xanh.

Đối với một cái gì đó đơn giản như ví dụ bạn đã cung cấp, việc triển khai ban đầu của bạn vẫn ổn vì nó nhỏ, nhưng thiết kế đơn giản này sẽ trở nên khó hiểu và dễ vỡ nếu bạn cố gắng đưa quá nhiều tính linh hoạt chức năng (trái với tính linh hoạt của thiết kế) vào một thủ tục. Dưới đây là lời giải thích của tôi về cách tiếp cận ưa thích của tôi để thiết kế các hệ thống phức tạp để dễ hiểu và linh hoạt mà tôi hy vọng sẽ chứng minh những gì tôi muốn nói với họ. Tôi sẽ không sử dụng chiến lược này cho một cái gì đó có thể được viết dưới 20 dòng trong một quy trình duy nhất bởi vì một cái gì đó quá nhỏ đã đáp ứng các tiêu chí của tôi về tính dễ hiểu và linh hoạt.


Đối tượng, không thủ tục

Thay vì sử dụng các lớp như các mô-đun trường học cũ với một loạt các thói quen bạn gọi để thực hiện những việc mà phần mềm của bạn nên làm, hãy xem xét mô hình hóa miền như các đối tượng tương tác và hợp tác để hoàn thành nhiệm vụ trong tay. Các phương thức trong mô hình hướng đối tượng ban đầu được tạo ra là tín hiệu giữa các đối tượng để Object1có thể nói Object2để thực hiện công việc của nó, bất kể đó là gì và có thể nhận được tín hiệu trả về. Điều này là do mô hình hướng đối tượng vốn dĩ là về mô hình hóa các đối tượng miền của bạn và các tương tác của chúng chứ không phải là một cách thức lạ mắt để tổ chức các chức năng và quy trình cũ của mô hình mệnh lệnh. Trong trường hợpvoid destroyBaghdadví dụ, thay vì cố gắng viết một phương pháp chung chung theo ngữ cảnh để xử lý sự phá hủy Baghdad hoặc bất kỳ thứ gì khác (có thể nhanh chóng phát triển phức tạp, khó hiểu và dễ vỡ), mọi thứ có thể bị phá hủy phải chịu trách nhiệm về cách hiểu để tự hủy diệt Ví dụ: bạn có một giao diện mô tả hành vi của những thứ có thể bị phá hủy:

interface Destroyable {
    void destroy();
}

Sau đó, bạn có một thành phố thực hiện giao diện này:

class City implements Destroyable {
    @Override
    public void destroy() {
        ...code that destroys the city
    }
}

Không có gì kêu gọi phá hủy một trường hợp Citysẽ quan tâm đến việc điều đó xảy ra như thế nào, vì vậy không có lý do gì để mã đó tồn tại ở bất cứ đâu bên ngoài City::destroy, và thực sự, kiến ​​thức sâu sắc về hoạt động Citybên trong của chính nó sẽ được kết hợp chặt chẽ làm giảm felxibility vì bạn phải xem xét các yếu tố bên ngoài mà bạn cần phải sửa đổi hành vi của City. Đây là mục đích thực sự đằng sau việc đóng gói. Hãy nghĩ về nó giống như mọi đối tượng có API riêng của nó sẽ cho phép bạn làm bất cứ điều gì bạn cần với nó để bạn có thể để nó lo lắng về việc thực hiện các yêu cầu của bạn.

Đoàn, không "Kiểm soát"

Bây giờ, cho dù lớp thực hiện của bạn là Cityhay Baghdadphụ thuộc vào quá trình phá hủy thành phố chung chung như thế nào. Trong tất cả các khả năng, một Citymảnh sẽ bao gồm các mảnh nhỏ hơn sẽ cần phải bị phá hủy riêng lẻ để hoàn thành việc phá hủy toàn bộ thành phố, vì vậy trong trường hợp đó, mỗi mảnh đó cũng sẽ thực hiện Destroyablevà mỗi thứ sẽ được hướng dẫn Cityphá hủy chính họ cũng giống như ai đó từ bên ngoài yêu cầu Citytự hủy diệt.

interface Part extends Destroyable {
    ...part-specific methods
}

class Building implements Part {
    ...part-specific methods
    @Override
    public void destroy() {
       ...code to destroy a building
    }
}

class Street implements Part {
    ...part-specific methods
    @Override
    public void destroy() {
        ...code to destroy a building
    }
}

class City implements Destroyable {
    public List<Part> parts() {...}

    @Override
    public void destroy() {
        parts().forEach(Destroyable::destroy);            
    }
}

Nếu bạn muốn trở nên thực sự điên rồ và thực hiện ý tưởng Bombvề một địa điểm bị rơi trên một địa điểm và phá hủy mọi thứ trong một bán kính nhất định, nó có thể trông giống như thế này:

class Bomb {
    private final Integer radius;

    public Bomb(final Integer radius) {
        this.radius = radius;
    }

    public void drop(final Grid grid, final Coordinate target) {
        new ObjectsByRadius(
            grid,
            target,
            this.radius
        ).forEach(Destroyable::destroy);
    }
}

ObjectsByRadiusđại diện cho một tập hợp các đối tượng được tính toán Bombtừ đầu vào vì Bombkhông quan tâm đến việc tính toán đó được thực hiện như thế nào miễn là nó có thể hoạt động với các đối tượng. Điều này có thể tái sử dụng một cách tình cờ, nhưng mục tiêu chính là tách biệt tính toán khỏi các quá trình thả Bombvà phá hủy các vật thể để bạn có thể hiểu từng mảnh và cách chúng khớp với nhau và thay đổi hành vi của một mảnh riêng lẻ mà không phải định hình lại toàn bộ thuật toán .

Tương tác, không phải thuật toán

Thay vì cố gắng đoán đúng số lượng tham số cho một thuật toán phức tạp, sẽ hợp lý hơn khi mô hình hóa quá trình như một tập hợp các đối tượng tương tác, mỗi đối tượng có vai trò cực kỳ hẹp, vì nó sẽ cho bạn khả năng mô hình hóa độ phức tạp của bạn xử lý thông qua các tương tác giữa các đối tượng được xác định rõ ràng, dễ hiểu và gần như không thay đổi. Khi được thực hiện một cách chính xác, điều này làm cho ngay cả một số sửa đổi phức tạp nhất cũng không quan trọng bằng việc thực hiện một hoặc hai giao diện và làm lại các đối tượng nào được khởi tạo trong main()phương thức của bạn .

Tôi sẽ đưa cho bạn một cái gì đó với ví dụ ban đầu của bạn, nhưng thực lòng tôi không thể hiểu ý nghĩa của việc "in ... Tiết kiệm ánh sáng ban ngày". Điều tôi có thể nói về loại vấn đề đó là bất cứ khi nào bạn thực hiện phép tính, kết quả của nó có thể được định dạng theo một số cách, cách ưa thích của tôi để phá vỡ nó là như sau:

interface Result {
    String print();
}

class Caclulation {
    private final Parameter paramater1;

    private final Parameter parameter2;

    public Calculation(final Parameter parameter1, final Parameter parameter2) {
        this.parameter1 = parameter1;
        this.parameter2 = parameter2;
    }

    public Result calculate() {
        ...calculate the result
    }
}

class FormattedResult {
    private final Result result;

    public FormattedResult(final Result result) {
        this.result = result;
    }

    @Override
    public String print() {
        ...interact with this.result to format it and return the formatted String
    }
}

Vì ví dụ của bạn sử dụng các lớp từ thư viện Java không hỗ trợ thiết kế này, bạn chỉ có thể sử dụng API ZonedDateTimetrực tiếp. Ý tưởng ở đây là mỗi phép tính được gói gọn trong đối tượng của chính nó. Nó không đưa ra giả định về việc nên chạy bao nhiêu lần hoặc định dạng kết quả như thế nào. Nó chỉ liên quan đến việc thực hiện các hình thức tính toán đơn giản nhất. Điều này làm cho nó dễ hiểu và linh hoạt để thay đổi. Tương tự như vậy, Resultđặc biệt quan tâm đến việc đóng gói kết quả của phép tính và FormattedResultđặc biệt quan tâm đến việc tương tác với Resultđịnh dạng của nó theo các quy tắc chúng tôi xác định. Theo cách này,chúng ta có thể tìm thấy số lượng đối số hoàn hảo cho mỗi phương thức của mình vì mỗi phương thức đều có một nhiệm vụ được xác định rõ . Việc sửa đổi chuyển tiếp về phía trước cũng đơn giản hơn nhiều, miễn là các giao diện không thay đổi (điều mà chúng không có khả năng thực hiện nếu bạn giảm thiểu trách nhiệm của các đối tượng của mình). main()Phương phápcủa chúng tôicó thể trông như thế này:

class App {
    public static void main(String[] args) {
        final List<Set<Paramater>> parameters = ...instantiated from args
        parameters.forEach(set -> {
            System.out.println(
                new FormattedResult(
                    new Calculation(
                        set.get(0),
                        set.get(1)
                    ).calculate()
                ).print()
            );
        });
    }
}

Như một vấn đề thực tế, Lập trình hướng đối tượng được phát minh cụ thể như một giải pháp cho vấn đề phức tạp / linh hoạt của mô hình mệnh lệnh vì không có câu trả lời hay (dù sao ai cũng có thể đồng ý hoặc đến độc lập) bằng cách nào để tối ưu hóa chỉ định các hàm và thủ tục bắt buộc trong thành ngữ.


Đây là một câu trả lời rất chi tiết và suy nghĩ, nhưng thật không may, tôi nghĩ rằng nó bỏ lỡ dấu hiệu về những gì OP thực sự yêu cầu. Anh ấy đã không yêu cầu một bài học về các thực hành OOP tốt để giải quyết ví dụ cụ thể của mình, anh ấy đã hỏi về các tiêu chí để chúng tôi quyết định đầu tư thời gian vào một giải pháp so với khái quát hóa.
maple_shaft

@maple_shaft Có thể tôi đã bỏ lỡ dấu, nhưng tôi nghĩ bạn cũng vậy. OP không hỏi về đầu tư thời gian so với khái quát hóa. Anh ta hỏi "Làm thế nào để tôi biết cách sử dụng lại các phương pháp của mình?" Anh ta tiếp tục hỏi trong câu hỏi của mình, "Nếu phá hủy thành phố (thành phố) tốt hơn phá hủyBaghdad (), thì TakeActionOnCity (hành động hành động, thành phố thành phố) thậm chí còn tốt hơn? Tại sao / tại sao không?" Tôi đã tạo ra một trường hợp cho một cách tiếp cận thay thế cho các giải pháp kỹ thuật mà tôi tin rằng giải quyết vấn đề tìm ra cách thức chung để tạo ra các phương pháp và cung cấp các ví dụ để hỗ trợ cho yêu cầu của tôi. Tôi xin lỗi bạn đã không thích nó.
Stuporman

@maple_shaft Thành thật mà nói, chỉ OP mới có thể đưa ra quyết định nếu câu trả lời của tôi có liên quan đến câu hỏi của anh ấy vì phần còn lại chúng tôi có thể chiến đấu bảo vệ những diễn giải của chúng tôi về ý định của anh ấy, tất cả đều có thể sai như nhau.
Stuporman

@maple_shaft Tôi đã thêm một phần giới thiệu để cố gắng làm rõ nó liên quan đến câu hỏi như thế nào và cung cấp một phân định rõ ràng giữa câu trả lời và việc thực hiện ví dụ. Điều đó có tốt hơn không?
Stuporman

1
Thành thật mà nói, nếu bạn áp dụng tất cả các nguyên tắc này, câu trả lời sẽ đến một cách tự nhiên, trôi chảy và có thể đọc được một cách ồ ạt. Thêm vào đó, thay đổi mà không có nhiều phiền phức. Tôi không biết bạn là ai, nhưng tôi ước có nhiều bạn hơn! Tôi tiếp tục trích xuất mã Reactive cho OO phong nha, và nó LUÔN có một nửa kích thước, dễ đọc hơn, dễ kiểm soát hơn và vẫn có luồng / tách / ánh xạ. Tôi nghĩ React dành cho những người không hiểu các khái niệm "cơ bản" mà bạn vừa liệt kê.
Stephen J

3

Kinh nghiệm , kiến thức tên miềnđánh giá mã.

Và, bất kể bạn có bao nhiêu hoặc ít kinh nghiệm , kiến thức về miền hoặc nhóm bạn có, bạn không thể tránh khỏi việc phải cấu trúc lại khi cần thiết.


Với Trải nghiệm , bạn sẽ bắt đầu nhận ra các mẫu trong miền - các phương thức không đặc hiệu (và các lớp) bạn viết. Và, nếu bạn hoàn toàn hứng thú với mã DRY, bạn sẽ cảm thấy tồi tệ khi bạn viết về một phương pháp mà theo bản năng bạn sẽ biết bạn sẽ viết các biến thể trong tương lai. Vì vậy, thay vào đó , bạn sẽ trực tiếp viết một mẫu số chung ít tham số nhất.

(Trải nghiệm này có thể chuyển qua bản năng vào một số đối tượng và phương thức miền của bạn.)

Với Kiến thức miền , bạn sẽ có ý thức về các khái niệm kinh doanh có liên quan chặt chẽ với nhau, khái niệm nào có các biến, khá tĩnh, v.v.

Với Đánh giá mã, nhiều khả năng sẽ bị bắt trước khi nó trở thành mã sản xuất, bởi vì đồng nghiệp của bạn sẽ (hy vọng) có những trải nghiệm và quan điểm độc đáo, cả về miềnmã hóa nói chung.


Điều đó nói rằng, các nhà phát triển mới thường sẽ không có các Spidey Senses này hoặc một nhóm các đồng nghiệp có kinh nghiệm để dựa vào ngay lập tức. Và, ngay cả các nhà phát triển có kinh nghiệm cũng được hưởng lợi từ một chuyên ngành cơ bản để hướng dẫn họ vượt qua các yêu cầu mới - hoặc qua những ngày sương mù não. Vì vậy, đây là những gì tôi muốn đề xuất khi bắt đầu :

  • Bắt đầu với việc thực hiện ngây thơ, với tham số tối thiểu.
    (Bao gồm bất kỳ tham số nào bạn đã biết bạn sẽ cần, rõ ràng ...)
  • Xóa các số và chuỗi ma thuật, di chuyển chúng đến cấu hình và / hoặc tham số
  • Các phương thức "lớn" thành các phương thức nhỏ hơn, được đặt tên tốt
  • Tái cấu trúc các phương thức dự phòng cao (nếu thuận tiện) thành mẫu số chung, tham số hóa các khác biệt.

Các bước này không nhất thiết phải xảy ra theo thứ tự đã nêu. Nếu bạn ngồi viết một phương thức mà bạn đã biết là rất dư thừa với một phương thức hiện có , hãy nhảy thẳng vào tái cấu trúc nếu nó thuận tiện. (Nếu nó sẽ không mất nhiều thời gian hơn để tái cấu trúc hơn là chỉ viết, kiểm tra và duy trì hai phương thức.)

Nhưng, ngoài việc chỉ có nhiều kinh nghiệm và cứ thế, tôi khuyên mã DRY-ing khá tối giản. Không khó để tái cấu trúc các vi phạm rõ ràng. Và, nếu bạn quá nhiệt tình , bạn có thể kết thúc với mã "quá KHÔ" thậm chí còn khó đọc, hiểu và duy trì hơn so với tương đương "WET".


2
Vì vậy, không có câu trả lời đúng If destoryCity(City city) is better than destoryBaghdad(), is takeActionOnCity(Action action, City city) even better?? Đó là một câu hỏi có / không, nhưng nó không có câu trả lời, phải không? Hoặc giả định ban đầu là sai, destroyCity(City)có thể không nhất thiết phải tốt hơn và nó thực sự phụ thuộc ? Vì vậy, điều đó không có nghĩa là tôi không phải là một kỹ sư phần mềm không được đào tạo về đạo đức bởi vì tôi trực tiếp thực hiện mà không có bất kỳ tham số nào ngay từ đầu? Ý tôi là câu trả lời cho câu hỏi cụ thể mà tôi đang hỏi là gì?
Koray Tugay

Câu hỏi của bạn sắp xếp một vài câu hỏi Câu trả lời cho câu hỏi tiêu đề là "kinh nghiệm, kiến ​​thức tên miền, đánh giá mã ... và ... đừng ngại tái cấu trúc." Câu trả lời cho bất kỳ câu hỏi cụ thể nào "là những tham số phù hợp cho phương pháp ABC" là ... "Tôi không biết. Tại sao bạn lại hỏi? Có điều gì đó không đúng với số lượng tham số hiện tại không ??? nó. Sửa nó đi. " ... Tôi có thể giới thiệu bạn đến "POAP" để được hướng dẫn thêm: Bạn cần hiểu lý do tại sao bạn đang làm những gì bạn đang làm!
Svidgen

Ý tôi là ... chúng ta thậm chí lùi một bước khỏi destroyBaghdad()phương pháp. Bối cảnh là gì? Đây có phải là một trò chơi video mà kết thúc trò chơi dẫn đến việc Baghdad bị phá hủy ??? Nếu vậy ... destroyBaghdad()có thể là một tên phương thức / chữ ký hoàn toàn hợp lý ...
svidgen 27/11/18

1
Vì vậy, bạn không đồng ý với trích dẫn trong câu hỏi của tôi, phải không? It should be noted that no ethically-trained software engineer would ever consent to write a DestroyBaghdad procedure.Nếu bạn ở trong phòng với Nathaniel Borenstein, bạn sẽ tranh luận với anh ta rằng điều đó thực sự phụ thuộc và tuyên bố của anh ta không đúng? Ý tôi là thật đẹp khi nhiều người đang trả lời, dành thời gian và sức lực của họ, nhưng tôi không thấy một câu trả lời cụ thể nào ở bất cứ đâu. Spidey-Senses, code review .. Nhưng câu trả lời là is takeActionOnCity(Action action, City city) better? nullgì?
Koray Tugay

1
@svidgen Tất nhiên, một cách khác để trừu tượng hóa điều này mà không cần nỗ lực thêm là đảo ngược sự phụ thuộc - có chức năng trả về một danh sách các thành phố, thay vì thực hiện bất kỳ hành động nào đối với chúng (như "println" trong mã gốc). Điều này có thể được tóm tắt thêm nếu cần thiết, nhưng chỉ cần một thay đổi này tự xử lý khoảng một nửa các yêu cầu được thêm vào trong câu hỏi ban đầu - và thay vì một hàm không tinh khiết có thể thực hiện tất cả các loại nội dung xấu, bạn chỉ có một chức năng trả về một danh sách và người gọi thực hiện công cụ xấu.
Luaan

2

Câu trả lời tương tự như với chất lượng, khả năng sử dụng, nợ kỹ thuật, v.v .:

Có thể tái sử dụng như bạn, người dùng, 1 cần chúng để được

Về cơ bản, đó là một lời kêu gọi phán xét - liệu chi phí thiết kế và duy trì sự trừu tượng sẽ được trả bằng chi phí (= thời gian và công sức) nó sẽ giúp bạn tiết kiệm chi phí.

  • Lưu ý cụm từ "xuống dòng": có một cơ chế hoàn trả ở đây, vì vậy nó sẽ phụ thuộc vào mức độ bạn sẽ làm việc với mã này hơn nữa. Ví dụ:
    • Đây có phải là một dự án một lần, hoặc nó sẽ được cải thiện dần dần trong một thời gian dài?
    • Bạn có tự tin vào thiết kế của mình không, hoặc bạn có thể sẽ phải bỏ đi hoặc thay đổi mạnh mẽ nó cho dự án / cột mốc tiếp theo (ví dụ: thử một khung khác)?
  • Lợi ích dự kiến ​​cũng phụ thuộc vào khả năng dự đoán tương lai của bạn (thay đổi ứng dụng). Đôi khi, bạn có thể thấy hợp lý địa điểm mà ứng dụng của bạn sẽ thực hiện. Nhiều lần hơn, bạn nghĩ rằng bạn có thể nhưng bạn thực sự không thể. Các quy tắc của ngón tay cái ở đây là nguyên tắc YAGNIquy tắc ba - cả hai đều nhấn mạnh vào việc làm những gì bạn biết, bây giờ.

1 Đây là cấu trúc mã, vì vậy bạn là "người dùng" trong trường hợp này - người dùng mã nguồn


1

Có một quy trình rõ ràng bạn có thể làm theo:

  • Viết một bài kiểm tra thất bại cho một tính năng mà bản thân nó là một "thứ" (nghĩa là không phải là sự phân chia tùy ý của một tính năng trong đó một nửa không thực sự có ý nghĩa).
  • Viết mã tối thiểu tuyệt đối để làm cho nó chuyển qua màu xanh lá cây, không phải là một dòng nhiều hơn.
  • Rửa sạch và lặp lại.
  • (Tái cấu trúc không ngừng nếu cần thiết, điều này sẽ dễ dàng do phạm vi kiểm tra tuyệt vời.)

Điều này xuất hiện với - ít nhất là theo ý kiến ​​của một số người - khá nhiều mã tối ưu, vì nó càng nhỏ càng tốt, mỗi tính năng hoàn thành mất ít thời gian nhất có thể (điều này có thể đúng hoặc không đúng nếu bạn nhìn vào kết thúc sản phẩm sau khi tái cấu trúc), và nó có phạm vi kiểm tra rất tốt. Nó cũng đáng chú ý tránh các phương thức hoặc các lớp quá chung chung.

Điều này cũng cung cấp cho bạn hướng dẫn rất rõ ràng khi làm cho mọi thứ chung chung và khi nào để chuyên môn.

Tôi thấy ví dụ thành phố của bạn kỳ lạ; Tôi rất có thể sẽ không bao giờ mã hóa tên thành phố. Rõ ràng là các thành phố bổ sung sẽ được đưa vào sau, bất kể bạn đang làm gì. Nhưng một ví dụ khác sẽ là màu sắc. Trong một số trường hợp, mã hóa "đỏ" hoặc "xanh" sẽ là một khả năng. Ví dụ, đèn giao thông là một màu phổ biến đến mức bạn có thể tránh xa nó (và bạn luôn có thể tái cấu trúc). Sự khác biệt là "đỏ" và "xanh" có ý nghĩa phổ quát, "mã hóa cứng" trong thế giới của chúng ta, thật khó có khả năng nó sẽ thay đổi, và thực sự cũng không có gì thay thế.

Phương pháp tiết kiệm ánh sáng ban ngày đầu tiên của bạn chỉ đơn giản là bị hỏng. Mặc dù phù hợp với các thông số kỹ thuật, mã hóa cứng 2018 đặc biệt xấu vì a) nó không được đề cập trong "hợp đồng" kỹ thuật (trong tên phương thức, trong trường hợp này) và b) sẽ sớm bị lỗi thời, vì vậy sẽ bị hỏng được bao gồm từ đi Đối với những thứ liên quan đến thời gian / ngày, sẽ rất hiếm khi có ý nghĩa để mã hóa cứng một giá trị cụ thể vì, tốt, thời gian tiếp tục. Nhưng ngoài việc đó, mọi thứ khác được đưa ra để thảo luận. Nếu bạn cho nó một năm đơn giản và sau đó luôn tính năm hoàn thành, hãy tiếp tục. Hầu hết những điều bạn đã liệt kê (định dạng, lựa chọn phạm vi nhỏ hơn, v.v.) hét lên rằng phương thức của bạn đang làm quá nhiều và thay vào đó có thể trả về một danh sách / mảng các giá trị để người gọi có thể tự định dạng / lọc.

Nhưng vào cuối ngày, hầu hết đây là ý kiến, sở thích, kinh nghiệm và thành kiến ​​cá nhân, vì vậy đừng băn khoăn quá nhiều về nó.


Về đoạn thứ hai đến đoạn cuối của bạn - hãy xem các "yêu cầu" ban đầu được đưa ra, tức là những yêu cầu mà phương pháp đầu tiên dựa trên. Nó chỉ định năm 2018, vì vậy mã này đúng về mặt kỹ thuật (và có thể sẽ phù hợp với phương pháp tiếp cận theo hướng tính năng của bạn).
dwizum

@dwizum, nó đúng về các yêu cầu, nhưng có tên phương thức là sai lệch. Vào năm 2019, bất kỳ lập trình viên nào chỉ nhìn vào tên phương thức sẽ cho rằng nó đang làm bất cứ điều gì (có thể trả về các giá trị cho năm hiện tại), chứ không phải 2018 ... Tôi sẽ thêm một câu vào câu trả lời để làm rõ hơn ý của tôi.
AnoE

1

Tôi đã có ý kiến ​​rằng có hai loại mã có thể tái sử dụng:

  • Mã có thể tái sử dụng vì đó là một điều cơ bản, cơ bản.
  • Mã có thể tái sử dụng vì nó có các tham số, ghi đè và móc cho mọi nơi.

Loại tái sử dụng đầu tiên thường là một ý tưởng tốt. Nó áp dụng cho những thứ như danh sách, hashmap, lưu trữ khóa / giá trị, bộ so khớp chuỗi (ví dụ regex, global, ...), bộ dữ liệu, thống nhất, cây tìm kiếm (chiều sâu đầu tiên, chiều rộng đầu tiên, độ sâu lặp lại, ...) , trình kết hợp trình phân tích cú pháp, bộ nhớ cache / bộ nhớ, trình đọc / ghi định dạng dữ liệu (s-biểu thức, XML, JSON, protobuf, ...), hàng đợi tác vụ, v.v.

Những thứ này rất chung chung, theo một cách rất trừu tượng, chúng được sử dụng lại khắp nơi trong lập trình hàng ngày. Nếu bạn thấy mình viết mã mục đích đặc biệt sẽ đơn giản hơn nếu nó được làm cho trừu tượng / chung chung hơn (ví dụ: nếu chúng tôi có "danh sách đơn đặt hàng của khách hàng", chúng tôi có thể vứt bỏ "thứ tự khách hàng" để lấy "danh sách" ) sau đó nó có thể là một ý tưởng tốt để kéo nó ra. Ngay cả khi nó không được sử dụng lại, nó cho phép chúng ta tách rời chức năng không liên quan.

Loại thứ hai là nơi chúng ta có một số mã cụ thể, giải quyết một vấn đề thực sự, nhưng làm như vậy bằng cách đưa ra một loạt các quyết định. Chúng ta có thể làm cho nó trở nên tổng quát / có thể tái sử dụng hơn bằng cách "mã hóa mềm" các quyết định đó, ví dụ như biến chúng thành các tham số, làm phức tạp việc thực hiện và nướng trong các chi tiết cụ thể hơn (ví dụ kiến ​​thức về móc mà chúng ta có thể muốn ghi đè). Ví dụ của bạn dường như thuộc loại này. Vấn đề với loại tái sử dụng này là cuối cùng chúng ta có thể cố gắng đoán các trường hợp sử dụng của người khác hoặc bản thân trong tương lai của chúng ta. Cuối cùng, chúng tôi có thể có rất nhiều tham số mà mã của chúng tôi không thể sử dụng được, hãy để một mình tái sử dụng! Nói cách khác, khi gọi nó cần nhiều nỗ lực hơn là chỉ viết phiên bản của chúng ta. Đây là nơi YAGNI (You Ain't Gonna Need It) quan trọng. Nhiều lần, những nỗ lực như vậy đối với mã "có thể tái sử dụng" cuối cùng không được sử dụng lại, vì nó có thể không phù hợp với những trường hợp sử dụng đó về cơ bản hơn các tham số có thể giải thích, hoặc những người dùng tiềm năng đó sẽ tự cuộn mình (heck, nhìn vào tất cả các tiêu chuẩn và thư viện ngoài kia có tác giả có tiền tố từ "Đơn giản", để phân biệt chúng với các tiền thân!).

Hình thức "tái sử dụng" thứ hai này về cơ bản nên được thực hiện trên cơ sở khi cần thiết. Chắc chắn, bạn có thể dán một số thông số "rõ ràng" trong đó, nhưng đừng bắt đầu cố gắng dự đoán tương lai. YAGNI.


Chúng tôi có thể nói rằng bạn đồng ý rằng lần đầu tiên của tôi là tốt, trong đó ngay cả năm đã được mã hóa cứng? Hoặc nếu ban đầu bạn thực hiện yêu cầu đó, bạn sẽ biến năm đó thành tham số trong lần đầu tiên của mình chứ?
Koray Tugay

Lần đầu tiên của bạn là tốt, vì yêu cầu là một kịch bản một lần để 'kiểm tra một cái gì đó'. Nó thất bại trong bài kiểm tra 'đạo đức' của cô, nhưng cô thất bại trong bài kiểm tra 'không giáo điều'. "Cô ấy có thể nói ..." đang phát minh ra những yêu cầu mà bạn không cần.
Warbo

Chúng tôi không thể nói 'phá hủy thành phố' là "tốt hơn" mà không có thêm thông tin: destroyBaghdadlà một kịch bản một lần (hoặc ít nhất, đó là sự bình thường). Có thể phá hủy bất kỳ thành phố nào sẽ là một sự cải tiến, nhưng điều gì sẽ xảy ra nếu destroyBaghdadlàm ngập lụt con hổ? Điều đó có thể được tái sử dụng cho Mosul và Basra, nhưng không phải cho Mecca hoặc Atlanta.
Warbo

Tôi thấy vì vậy bạn không đồng ý với Nathaniel Borenstein, chủ sở hữu của trích dẫn. Tôi đang cố gắng để hiểu từ từ Tôi nghĩ bằng cách đọc tất cả những phản hồi và các cuộc thảo luận.
Koray Tugay

1
Tôi thích sự khác biệt này. Không phải lúc nào cũng rõ ràng và luôn có "trường hợp biên giới". Nhưng nói chung, tôi cũng là một fan hâm mộ của "khối xây dựng" (thường ở dạng staticphương thức) hoàn toàn có chức năng và mức độ thấp, và ngược lại với điều đó, quyết định về "cấu hình tham số và móc" thường là ở chỗ bạn phải xây dựng một số cấu trúc yêu cầu được chứng minh.
Marco13

1

Đã có nhiều câu trả lời xuất sắc và công phu. Một số trong số chúng đi sâu vào chi tiết cụ thể, đưa ra các quan điểm nhất định về phương pháp phát triển phần mềm nói chung và một số trong số chúng chắc chắn có các yếu tố gây tranh cãi hoặc "ý kiến" được đưa vào.

Câu trả lời của Warbo đã chỉ ra các loại tái sử dụng khác nhau . Cụ thể, cho dù một cái gì đó có thể tái sử dụng bởi vì nó là một khối xây dựng cơ bản, hoặc liệu một cái gì đó có thể tái sử dụng bởi vì nó là "chung chung" theo một cách nào đó. Đề cập đến cái sau, có một cái gì đó mà tôi coi là một biện pháp để tái sử dụng:

Cho dù một phương pháp có thể mô phỏng một phương pháp khác.

Về ví dụ từ câu hỏi: Hãy tưởng tượng rằng phương pháp

void dayLightSavings()

là việc thực hiện một chức năng được yêu cầu bởi một khách hàng. Vì vậy, nó sẽ là thứ mà các lập trình viên khác phải sử dụng , và do đó, là một phương thức công khai , như trong

publicvoid dayLightSavings()

Điều này có thể được thực hiện như bạn thể hiện trong câu trả lời của bạn. Bây giờ, ai đó muốn tham số hóa nó với năm. Vì vậy, bạn có thể thêm một phương thức

publicvoid dayLightSavings(int year)

và thay đổi cách thực hiện ban đầu thành

public void dayLightSavings() {
    dayLightSavings(2018);
}

"Yêu cầu tính năng" tiếp theo và khái quát hóa theo cùng một mẫu. Vì vậy, nếuchỉ khi có nhu cầu về hình thức chung nhất, bạn có thể triển khai nó, biết rằng hình thức chung nhất này cho phép thực hiện tầm thường của những hình thức cụ thể hơn:

public void dayLightSavings() {
    dayLightSavings(2018, 0, 12, 0, 12, new DateTimeFormatter(...));
}

Nếu bạn đã dự đoán các tiện ích mở rộng và yêu cầu tính năng trong tương lai và có một chút thời gian theo ý bạn và muốn dành một ngày cuối tuần nhàm chán với những khái quát (có khả năng vô dụng), bạn có thể đã bắt đầu với một cái chung nhất ngay từ đầu. Nhưng chỉ là một phương pháp riêng tư . Miễn là bạn chỉ tiếp xúc với phương thức đơn giản được khách hàng yêu cầu là phương thức công khai , bạn sẽ an toàn.

tl; dr :

Câu hỏi thực sự không phải là "phương pháp nên sử dụng lại như thế nào". Câu hỏi đặt ra là mức độ tái sử dụng này được phơi bày và API trông như thế nào. Tạo một API đáng tin cậy có thể chịu được thử thách của thời gian (ngay cả khi các yêu cầu tiếp theo xuất hiện sau) là một nghệ thuật và thủ công, và chủ đề quá phức tạp để đề cập đến nó ở đây. Hãy xem bài thuyết trình này của Joshua Bloch hoặc wiki thiết kế API để bắt đầu.


dayLightSavings()gọi điện thoại dayLightSavings(2018)dường như không phải là một ý tưởng tốt cho tôi.
Koray Tugay

@KorayTugay Khi yêu cầu ban đầu là nó sẽ in "tiết kiệm ánh sáng ban ngày cho năm 2018", thì nó vẫn ổn. Trong thực tế, đây chính xác phương pháp mà bạn thực hiện ban đầu. Nếu cần in "tiết kiệm ánh sáng ban ngày cho năm hiện tại , thì dĩ nhiên bạn sẽ gọi dayLightSavings(computeCurrentYear());. ...
Marco13

0

Một nguyên tắc nhỏ là: phương pháp của bạn nên có thể tái sử dụng như có thể tái sử dụng.

Nếu bạn mong đợi rằng bạn sẽ chỉ gọi phương thức của mình ở một nơi, thì nó sẽ chỉ có các tham số được biết đến với trang web cuộc gọi và không có sẵn cho phương thức này.

Nếu bạn có nhiều người gọi hơn, bạn có thể giới thiệu các tham số mới miễn là những người gọi khác có thể vượt qua các tham số đó; nếu không bạn cần phương pháp mới.

Vì số lượng người gọi có thể tăng lên theo thời gian, bạn cần chuẩn bị cho việc tái cấu trúc hoặc quá tải. Trong nhiều trường hợp, điều đó có nghĩa là bạn sẽ cảm thấy an toàn khi chọn biểu thức và chạy tham số trích xuất hành động của IDE của IDE.


0

Câu trả lời cực ngắn: Việc ghép ít hoặc phụ thuộc vào mã khác mà mô-đun chung của bạn có thể tái sử dụng nhiều hơn.

Ví dụ của bạn chỉ phụ thuộc vào

import java.time.*;
import java.util.Set;

vì vậy trong lý thuyết nó có thể tái sử dụng cao.

Trong thực tế tôi không nghĩ rằng bạn sẽ có một usecase thứ hai cần mã này vì vậy theo nguyên tắc yagni tôi sẽ không làm cho nó có thể sử dụng lại được nếu không có nhiều hơn 3 dự án khác nhau cần mã này.

Các khía cạnh khác của khả năng sử dụng lại là dễ sử dụng và cấu trúc phù hợp với Phát triển hướng thử nghiệm : Thật hữu ích nếu bạn có một bài kiểm tra đơn vị đơn giản chứng minh / tài liệu dễ dàng sử dụng mô-đun chung của bạn làm ví dụ mã hóa cho người dùng lib của bạn.


0

Đây là một cơ hội tốt để nêu một quy tắc tôi đặt ra gần đây:

Trở thành một lập trình viên giỏi đồng nghĩa với việc có thể dự đoán tương lai.

Tất nhiên, điều này là hoàn toàn không thể! Rốt cuộc, bạn không bao giờ biết chắc chắn những khái quát nào bạn sẽ thấy hữu ích sau này, những nhiệm vụ liên quan nào bạn muốn thực hiện, những tính năng mới mà người dùng của bạn sẽ muốn, & c. Nhưng kinh nghiệm đôi khi cho bạn một ý tưởng sơ bộ về những gì có thể có ích.

Các yếu tố khác mà bạn phải cân bằng với điều đó là có bao nhiêu thời gian và công sức sẽ tham gia, và nó sẽ tạo ra mã của bạn phức tạp hơn bao nhiêu. Đôi khi bạn may mắn, và giải quyết vấn đề tổng quát hơn thực sự đơn giản hơn! (Ít nhất là về mặt khái niệm, nếu không phải là số lượng mã.) Nhưng thường xuyên hơn, có một chi phí phức tạp cũng như một thời gian và nỗ lực.

Vì vậy, nếu bạn nghĩ rằng một sự khái quát hóa rất có thể là cần thiết, thì nó thường đáng để thực hiện (trừ khi nó làm tăng thêm nhiều công việc hoặc sự phức tạp); nhưng nếu nó có vẻ ít khả năng hơn thì có lẽ là không (trừ khi nó rất dễ và / hoặc đơn giản hóa mã).

(Ví dụ gần đây: tuần trước tôi đã được cung cấp thông số kỹ thuật. Các hành động mà hệ thống sẽ mất đúng 2 ngày sau khi hết hạn. Vì vậy, tất nhiên tôi đã tạo khoảng thời gian 2 ngày làm tham số. Tuần này, các doanh nhân rất vui mừng, vì họ Tôi đã yêu cầu sự cải thiện đó rồi! Tôi đã may mắn: đó là một sự thay đổi dễ dàng và tôi đoán nó rất có thể được mong muốn. Thường thì rất khó để đánh giá. Nhưng vẫn đáng để thử và dự đoán thường là một hướng dẫn tốt .)


0

Đầu tiên, câu trả lời tốt nhất cho 'Làm thế nào để tôi biết các phương pháp của mình nên sử dụng lại như thế nào? "Là" kinh nghiệm ". Làm điều này vài nghìn lần và bạn thường nhận được câu trả lời đúng. dòng của câu trả lời này: Khách hàng của bạn sẽ cho bạn biết mức độ linh hoạt và bao nhiêu lớp khái quát mà bạn nên tìm kiếm.

Nhiều câu trả lời có những lời khuyên cụ thể. Tôi muốn đưa ra một cái gì đó chung chung hơn ... bởi vì sự trớ trêu là quá nhiều niềm vui để vượt qua!

Như một số câu trả lời đã lưu ý, tính tổng quát là đắt đỏ. Tuy nhiên, nó thực sự không. Không phải lúc nào. Hiểu chi phí là điều cần thiết để chơi trò chơi tái sử dụng.

Tôi tập trung vào việc đặt mọi thứ lên một thang điểm từ "không thể đảo ngược" thành "có thể đảo ngược". Đó là một quy mô trơn tru. Điều duy nhất thực sự không thể đảo ngược là "thời gian dành cho dự án". Bạn sẽ không bao giờ lấy lại được những tài nguyên đó. Hơi ít có thể đảo ngược có thể là tình huống "còng tay vàng" như API Windows. Các tính năng không dùng nữa vẫn tồn tại trong API đó trong nhiều thập kỷ vì mô hình kinh doanh của Microsoft yêu cầu nó. Nếu bạn có những khách hàng có mối quan hệ sẽ bị hủy hoại vĩnh viễn bằng cách hoàn tác một số tính năng API, thì điều đó sẽ được coi là không thể đảo ngược. Nhìn vào đầu kia của thang đo, bạn có những thứ như mã nguyên mẫu. Nếu bạn không thích nơi nó đi, bạn có thể vứt nó đi. Ít có thể đảo ngược có thể là các API sử dụng nội bộ. Họ có thể được tái cấu trúc mà không làm phiền khách hàng,thời gian (tài nguyên không thể đảo ngược nhất trong tất cả!)

Vì vậy, đặt những thứ này trên một quy mô. Bây giờ bạn có thể áp dụng một heuristic: một thứ gì đó có thể đảo ngược, bạn càng có thể sử dụng nó cho các hoạt động tìm kiếm trong tương lai. Nếu một cái gì đó là không thể đảo ngược, chỉ sử dụng nó cho các nhiệm vụ cụ thể theo định hướng khách hàng. Đây là lý do tại sao bạn thấy các nguyên tắc giống như các nguyên tắc từ lập trình cực đoan, đề nghị chỉ làm những gì khách hàng yêu cầu và không có gì hơn. Những nguyên tắc này rất tốt trong việc đảm bảo bạn không làm điều gì đó mà bạn hối tiếc.

Những thứ như nguyên tắc DRY gợi ý một cách để di chuyển sự cân bằng đó. Nếu bạn thấy mình lặp lại chính mình, đó là cơ hội để tạo ra những gì về cơ bản là API nội bộ. Không có khách hàng nhìn thấy nó, vì vậy bạn luôn có thể thay đổi nó. Khi bạn có API nội bộ này, bây giờ bạn có thể bắt đầu chơi với những thứ nhìn về phía trước. Có bao nhiêu nhiệm vụ dựa trên múi giờ khác nhau mà bạn nghĩ rằng vợ bạn sẽ giao cho bạn? Bạn có bất kỳ khách hàng nào khác có thể muốn các nhiệm vụ dựa trên múi giờ không? Sự linh hoạt của bạn ở đây được mua bởi nhu cầu cụ thể của khách hàng hiện tại của bạn và nó hỗ trợ nhu cầu tiềm năng trong tương lai của khách hàng trong tương lai.

Phương pháp tư duy phân lớp này, xuất phát tự nhiên từ DRY tự nhiên cung cấp sự khái quát hóa mà bạn muốn mà không lãng phí. Nhưng có giới hạn không? Tất nhiên là có. Nhưng để nhìn thấy nó, bạn phải nhìn thấy rừng cây.

Nếu bạn có nhiều lớp linh hoạt, chúng thường dẫn đến việc thiếu kiểm soát trực tiếp các lớp đối diện với khách hàng của bạn. Tôi đã có phần mềm trong đó tôi có nhiệm vụ tàn bạo là giải thích cho khách hàng lý do tại sao họ không thể có thứ họ muốn do tính linh hoạt được xây dựng trong 10 lớp mà họ không bao giờ thấy. Chúng tôi viết mình vào một góc. Chúng tôi buộc mình trong một nút thắt với tất cả sự linh hoạt mà chúng tôi nghĩ rằng chúng tôi cần.

Vì vậy, khi bạn đang thực hiện thủ thuật khái quát hóa / DRY này, hãy luôn giữ nhịp đập cho khách hàng của bạn . Bạn nghĩ vợ bạn sẽ yêu cầu gì tiếp theo? Bạn đang đặt mình vào một vị trí để đáp ứng những nhu cầu đó? Nếu bạn có sở trường, khách hàng sẽ cho bạn biết một cách hiệu quả nhu cầu trong tương lai của họ. Nếu bạn không có sở trường, tốt, hầu hết chúng ta chỉ dựa vào phỏng đoán! (đặc biệt là với vợ hoặc chồng!) Một số khách hàng sẽ muốn có sự linh hoạt cao và sẵn sàng chấp nhận chi phí phát triển thêm của bạn với tất cả các lớp này vì họ được hưởng lợi trực tiếp từ tính linh hoạt của các lớp đó. Các khách hàng khác có các yêu cầu không cố định khá cố định và họ thích phát triển trực tiếp hơn. Khách hàng của bạn sẽ cho bạn biết mức độ linh hoạt và bao nhiêu lớp khái quát mà bạn nên tìm kiếm.


Cũng cần phải có những người khác thực hiện 10000 lần này, vậy tại sao tôi phải làm 10000 lần và tích lũy kinh nghiệm khi tôi có thể học hỏi từ những người khác? Bởi vì câu trả lời sẽ khác nhau đối với mỗi cá nhân nên câu trả lời từ kinh nghiệm không được áp dụng cho tôi? Ngoài ra Your customer will tell you how much flexibility and how many layers of generalization you should seek.thế giới này là gì?
Koray Tugay

@KorayTugay Đó là một thế giới kinh doanh. Nếu khách hàng của bạn không cho bạn biết phải làm gì, thì bạn đã không lắng nghe đủ. Tất nhiên, họ sẽ không luôn nói với bạn bằng lời, nhưng họ nói với bạn theo những cách khác. Kinh nghiệm giúp bạn lắng nghe những thông điệp tinh tế hơn của họ. Nếu bạn chưa có kỹ năng này, hãy tìm một người nào đó trong công ty bạn có kỹ năng lắng nghe những gợi ý khách hàng tinh tế đó và miệt mài tìm hướng đi. Ai đó sẽ có kỹ năng đó, ngay cả khi họ là Giám đốc điều hành hoặc tiếp thị.
Cort Ammon

Trong trường hợp cụ thể của bạn, nếu bạn không thể bỏ rác vì quá bận rộn để mã hóa phiên bản tổng quát của vấn đề múi giờ này thay vì hack cùng một giải pháp cụ thể, khách hàng của bạn sẽ cảm thấy thế nào?
Cort Ammon

Vì vậy, bạn đồng ý rằng cách tiếp cận đầu tiên của tôi là đúng, mã hóa 2018 trong lần đầu tiên thay vì tham số hóa năm? (btw, Tôi thực sự không lắng nghe khách hàng của tôi, ví dụ rác rưởi. Đó là hiểu khách hàng của bạn .. Ngay cả khi nhận được hỗ trợ từ The Oracle, không có tin nhắn tinh tế nào để nghe khi cô ấy nói tôi cần một danh sách ánh sáng ban ngày thay đổi cho năm 2018.) Cảm ơn bạn đã dành thời gian và trả lời btw.
Koray Tugay

@KorayTugay Không biết bất kỳ chi tiết bổ sung nào, tôi muốn nói mã hóa cứng là phương pháp phù hợp. Bạn không có cách nào để biết nếu bạn sẽ cần mã DLS trong tương lai, cũng không biết cô ấy có thể đưa ra yêu cầu gì tiếp theo. Và nếu khách hàng của bạn đang cố gắng kiểm tra bạn, họ sẽ nhận được những gì họ nhận được = D
Cort Ammon

0

không một kỹ sư phần mềm được đào tạo về đạo đức nào có thể đồng ý viết một thủ tục DestBaghdad. Thay vào đó, đạo đức nghề nghiệp cơ bản sẽ yêu cầu anh ta viết một thủ tục DestCity, trong đó Baghdad có thể được đưa ra như một tham số.

Đây là một cái gì đó được biết đến trong giới công nghệ phần mềm tiên tiến như một "trò đùa". Truyện cười không phải là những gì chúng ta gọi là "đúng", mặc dù để gây cười, họ thường phải gợi ý về điều gì đó đúng.

Trong trường hợp cụ thể này, "trò đùa" không phải là "sự thật". Công việc liên quan đến việc viết một quy trình chung để tiêu diệt bất kỳ thành phố nào là, chúng ta có thể giả định một cách an toàn, các lệnh có cường độ vượt quá yêu cầu để tiêu diệt một cụ thểthành phố. Mặt khác, bất cứ ai đã phá hủy một hoặc một số ít thành phố (Joshua trong Kinh thánh, chúng ta sẽ nói, hoặc Tổng thống Truman) có thể khái quát một cách tầm thường những gì họ đã làm và có thể phá hủy hoàn toàn bất kỳ thành phố nào theo ý muốn. Điều này trong thực tế không phải là trường hợp. Các phương pháp mà hai người nổi tiếng sử dụng để phá hủy một số lượng nhỏ các thành phố cụ thể sẽ không nhất thiết phải hoạt động trên bất kỳ thành phố nào vào bất kỳ lúc nào. Một thành phố khác có các bức tường có tần số cộng hưởng khác nhau hoặc có hệ thống phòng không trên không khá cao, sẽ cần những thay đổi nhỏ hoặc cơ bản của cách tiếp cận (một tiếng kèn khác nhau hoặc một tên lửa).

Điều này cũng dẫn đến việc bảo trì mã chống lại những thay đổi theo thời gian: có khá nhiều thành phố hiện nay sẽ không thuộc về những cách tiếp cận đó, nhờ các phương pháp xây dựng hiện đại và radar phổ biến.

Phát triển và thử nghiệm một phương tiện hoàn toàn chung có nghĩa là sẽ phá hủy bất kỳ thành phố nào , trước khi bạn đồng ý phá hủy chỉ một thành phố, là một cách tiếp cận không hiệu quả. Không một kỹ sư phần mềm được đào tạo đạo đức nào sẽ cố gắng khái quát hóa một vấn đề đến một mức độ đòi hỏi các đơn đặt hàng lớn hơn công việc so với chủ nhân / khách hàng của họ thực sự cần phải trả tiền, mà không cần phải chứng minh.

Vậy điều gì là đúng? Đôi khi thêm tính tổng quát là chuyện nhỏ. Chúng ta có nên luôn luôn thêm tính tổng quát khi nó không quan trọng để làm như vậy? Tôi vẫn sẽ tranh luận "không, không phải lúc nào", vì vấn đề bảo trì dài hạn. Giả sử rằng tại thời điểm viết bài, tất cả các thành phố về cơ bản là giống nhau, vì vậy tôi đi trước với DestCity. Khi tôi đã viết điều này, cùng với các bài kiểm tra tích hợp (do không gian đầu vào vô hạn) lặp đi lặp lại trên mỗi thành phố đã biết và đảm bảo chức năng hoạt động trên mỗi (không chắc cách thức hoạt động. Có thể gọi City.clone () và phá hủy bản sao? Dù sao đi nữa).

Trong thực tế, chức năng chỉ được sử dụng để tiêu diệt Baghdad, giả sử ai đó xây dựng một thành phố mới có khả năng chống lại các kỹ thuật của tôi (nó nằm sâu dưới lòng đất hoặc một cái gì đó). Bây giờ tôi có một thử nghiệm tích hợp cho một trường hợp sử dụng thậm chí không thực sự tồn tại và trước khi tôi có thể tiếp tục chiến dịch khủng bố chống lại thường dân vô tội ở Iraq, tôi phải tìm ra cách tiêu diệt Subterrania. Đừng bận tâm liệu điều này có đạo đức hay không, nó thật ngu ngốclãng phí thời gian kỳ dị của tôi .

Vì vậy, bạn có thực sự muốn một chức năng có thể tiết kiệm ánh sáng ban ngày cho bất kỳ năm nào , chỉ để xuất dữ liệu cho năm 2018? Có thể, nhưng chắc chắn sẽ cần một lượng nhỏ nỗ lực thêm chỉ để ghép các trường hợp thử nghiệm lại với nhau. Nó có thể đòi hỏi một nỗ lực lớn để có được cơ sở dữ liệu múi giờ tốt hơn cơ sở dữ liệu bạn thực sự có. Vì vậy, ví dụ vào năm 1908, thị trấn Port Arthur, Ontario đã có một khoảng thời gian DST bắt đầu vào ngày 1 tháng Bảy. Có phải trong cơ sở dữ liệu múi giờ của hệ điều hành của bạn? Nghĩ là không, vì vậy chức năng tổng quát của bạn là sai . Không có gì đặc biệt là đạo đức về việc viết mã mà hứa hẹn nó không thể giữ được.

Được rồi, vì vậy, với sự cẩn thận thích hợp, thật dễ dàng để viết một hàm làm múi giờ trong một vài năm, ví dụ như ngày 1970. Nhưng thật dễ dàng để thực hiện chức năng mà bạn thực sự đã viết và khái quát hóa nó để tham số hóa năm. Vì vậy, bây giờ thực sự không còn đạo đức / hợp lý để nói chung nữa, đó là làm những gì bạn đã làm và sau đó khái quát hóa nếu và khi bạn cần nó.

Tuy nhiên, nếu bạn biết lý do tại sao vợ bạn muốn kiểm tra danh sách DST này, thì bạn sẽ có một ý kiến ​​được thông báo về việc liệu cô ấy có thể hỏi lại câu hỏi tương tự vào năm 2019 hay không, và nếu vậy, liệu bạn có thể tự mình thoát khỏi vòng lặp đó hay không bằng cách đưa ra Đây là một chức năng mà cô ấy có thể gọi, mà không cần phải biên dịch lại. Khi bạn đã thực hiện phân tích đó, câu trả lời cho câu hỏi "có nên khái quát hóa cho những năm gần đây" có thể là "có". Nhưng bạn tạo ra một vấn đề khác cho chính mình, đó là dữ liệu múi giờ trong tương lai chỉ là tạm thời, và do đó nếu cô ấy điều hành nó cho năm 2019 ngày hôm nay, cô ấy có thể hoặc không thể nhận ra rằng nó đang cho cô ấy một dự đoán tốt nhất. Vì vậy, bạn vẫn phải viết một loạt tài liệu không cần thiết cho chức năng ít chung hơn ("dữ liệu đến từ cơ sở dữ liệu múi giờ blah blah đây là liên kết để xem chính sách của họ về việc cập nhật blah blah"). Nếu bạn từ chối trường hợp đặc biệt, thì tất cả thời gian bạn đang làm điều đó, cô ấy không thể tiếp tục với nhiệm vụ mà cô ấy cần dữ liệu năm 2018, vì một số điều vô nghĩa về năm 2019 cô ấy thậm chí không quan tâm.

Đừng làm những điều khó khăn mà không suy nghĩ kỹ, chỉ vì một trò đùa đã nói với bạn. Nó khá hữu ích? Là nó đủ rẻ cho mức độ hữu ích?


0

Đây là một đường dễ dàng để vẽ bởi vì khả năng sử dụng lại theo định nghĩa của các phi hành gia kiến ​​trúc là bollocks.

Hầu như tất cả các mã được tạo bởi các nhà phát triển ứng dụng là cực kỳ cụ thể. Đây không phải là năm 1980. Hầu hết mọi thứ đáng để bận tâm đã nằm trong một khuôn khổ.

Trừu tượng và quy ước đòi hỏi tài liệu và nỗ lực học tập. Vui lòng ngừng tạo ra những cái mới chỉ vì lợi ích của nó. (Tôi đang nhìn bạn , người JavaScript!)

Chúng ta hãy thỏa sức tưởng tượng rằng bạn đã tìm thấy thứ gì đó thực sự phải có trong khuôn khổ lựa chọn của bạn. Bạn không thể bật mã theo cách bạn thường làm. Ồ không, bạn cần bảo hiểm kiểm tra không chỉ cho mục đích sử dụng mà còn cho các lần khởi hành từ mục đích sử dụng, cho tất cả các trường hợp cạnh đã biết, cho tất cả các chế độ lỗi có thể tưởng tượng, các trường hợp kiểm tra để chẩn đoán, dữ liệu kiểm tra, tài liệu kỹ thuật, tài liệu người dùng, quản lý phát hành, hỗ trợ kịch bản, kiểm tra hồi quy, quản lý thay đổi ...

Chủ nhân của bạn có vui vẻ trả tiền cho tất cả không? Tôi sẽ nói không.

Trừu tượng là cái giá chúng ta phải trả cho sự linh hoạt. Nó làm cho mã của chúng tôi phức tạp hơn và khó hiểu hơn. Trừ khi tính linh hoạt phục vụ nhu cầu thực tế và hiện tại, không, vì YAGNI.

Chúng ta hãy xem một ví dụ thực tế mà tôi vừa phải đối phó: HTMLRenderer. Nó không đúng tỷ lệ khi tôi cố kết xuất vào bối cảnh thiết bị của máy in. Tôi phải mất cả ngày để phát hiện ra rằng theo mặc định, nó đã sử dụng GDI (không có tỷ lệ) thay vì GDI + (không, nhưng không phải là antialias) bởi vì tôi phải lội qua sáu cấp độ gián tiếp trong hai hội đồng trước khi tôi tìm thấy mã mà đã làm bất cứ điều gì .

Trong trường hợp này tôi sẽ tha thứ cho tác giả. Sự trừu tượng hóa thực sự cần thiết bởi vì đây mã khung nhắm vào năm mục tiêu kết xuất rất khác nhau: WinForms, WPF, dotnet Core, Mono và PdfSharp.

Nhưng điều đó chỉ nhấn mạnh quan điểm của tôi: bạn gần như chắc chắn không làm điều gì đó cực kỳ phức tạp (kết xuất HTML với biểu định kiểu) nhắm mục tiêu đến nhiều nền tảng với mục tiêu đã nêu là hiệu suất cao trên tất cả các nền tảng.

của bạn gần như chắc chắn là một lưới cơ sở dữ liệu khác với các quy tắc kinh doanh chỉ áp dụng cho chủ lao động và các quy tắc thuế chỉ áp dụng ở tiểu bang của bạn, trong một ứng dụng không được bán.

Tất cả những gì gián tiếp giải quyết một vấn đề bạn không có và làm cho mã của bạn nhiều khó khăn hơn để đọc, mà ồ ạt tăng chi phí bảo trì và là một tai hại rất lớn để sử dụng lao động của bạn. May mắn thay, những người nên phàn nàn về điều này không thể hiểu những gì bạn đang làm với họ.

Một lập luận phản đối là loại trừu tượng này hỗ trợ phát triển theo hướng kiểm tra, nhưng tôi nghĩ TDD cũng là một vấn đề, bởi vì nó giả định rằng doanh nghiệp có sự hiểu biết rõ ràng, đầy đủ và đúng đắn về các yêu cầu của nó. TDD rất tốt cho NASA và phần mềm điều khiển cho thiết bị y tế và xe tự lái, nhưng quá đắt đối với mọi người khác.


Ngẫu nhiên, không thể dự đoán tất cả các khoản tiết kiệm ánh sáng ban ngày trên thế giới. Israel nói riêng có khoảng 40 lần chuyển đổi mỗi năm nhảy khắp nơi vì chúng ta không thể có người cầu nguyện không đúng lúc và chúa không tiết kiệm ánh sáng ban ngày.


Mặc dù tôi đồng ý với những gì bạn nói về sự trừu tượng và nỗ lực học tập, và đặc biệt khi nó nhắm vào sự ghê tởm được gọi là "JavaScript", tôi hoàn toàn không đồng ý với câu đầu tiên. Khả năng sử dụng lại có thể xảy ra ở nhiều cấp độ, nó có thể đi quá xa , và mã ném có thể được viết bởi các lập trình viên vứt đi. Nhưng có những người có đủ lý tưởng để ít nhất nhằm vào mã tái sử dụng. Thật đáng tiếc cho bạn nếu bạn không thấy những lợi ích mà điều này có thể có.
Marco13

@ Marco13 - Vì sự phản đối của bạn là hợp lý nên tôi sẽ mở rộng quan điểm.
Peter Wone

-3

Nếu bạn đang sử dụng ít nhất java 8, bạn sẽ viết lớp WorldTimeZones để cung cấp những gì thực chất là tập hợp các múi giờ.

Sau đó thêm phương thức bộ lọc (Bộ lọc dự đoán) vào lớp WorldTimeZones. Điều này cung cấp khả năng cho người gọi lọc bất cứ thứ gì họ muốn bằng cách chuyển một biểu thức lambda làm tham số.

Về bản chất, phương thức bộ lọc duy nhất hỗ trợ lọc trên bất kỳ thứ gì có trong giá trị được truyền cho vị từ.

Cách khác, thêm phương thức stream () vào lớp WorldTimeZones của bạn để tạo luồng luồng thời gian khi được gọi. Sau đó, người gọi có thể lọc, ánh xạ và thu nhỏ theo ý muốn mà không cần bạn viết bất kỳ chuyên môn nào cả.


3
Đây là những ý tưởng khái quát tốt tuy nhiên câu trả lời này hoàn toàn bỏ lỡ dấu hiệu về những gì đang được hỏi trong câu hỏi. Câu hỏi không phải là làm thế nào để khái quát hóa tốt nhất giải pháp mà là nơi chúng ta vẽ đường ở mức khái quát hóa và cách chúng ta cân nhắc những cân nhắc này về mặt đạo đức.
maple_shaft

Vì vậy, tôi đang nói rằng bạn nên cân nhắc chúng theo số lượng các trường hợp sử dụng mà chúng hỗ trợ được sửa đổi bởi sự phức tạp của việc tạo. Một phương thức hỗ trợ một ca sử dụng không có giá trị như một phương thức hỗ trợ 20 ca sử dụng. Mặt khác, nếu tất cả những gì bạn biết là một trường hợp sử dụng và phải mất 5 phút để mã hóa cho nó - hãy thực hiện. Thông thường các phương pháp mã hóa hỗ trợ sử dụng cụ thể thông báo cho bạn cách tổng quát hóa.
Rodney P. Barbati
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.