Quá nhiều trừu tượng làm cho mã khó mở rộng


9

Tôi đang đối mặt với các vấn đề với những gì tôi cảm thấy có quá nhiều sự trừu tượng trong cơ sở mã (hoặc ít nhất là xử lý nó). Hầu hết các phương thức trong cơ sở mã đã được trừu tượng hóa để lấy cha mẹ A cao nhất trong cơ sở mã, nhưng con B của cha mẹ này có một thuộc tính mới ảnh hưởng đến logic của một số phương thức đó. Vấn đề là những thuộc tính đó không thể được kiểm tra trong các phương thức đó vì đầu vào được trừu tượng hóa thành A và dĩ nhiên A không có thuộc tính này. Nếu tôi cố gắng tạo một phương thức mới để xử lý B khác nhau, nó sẽ được gọi để sao chép mã. Đề xuất của lãnh đạo công nghệ của tôi là tạo ra một phương thức chia sẻ có các tham số boolean, nhưng vấn đề với điều này là một số người xem đây là "luồng điều khiển ẩn", trong đó phương thức chia sẻ có logic có thể không rõ ràng đối với các nhà phát triển trong tương lai , và phương thức chia sẻ này sẽ phát triển quá phức tạp / phức tạp một lần nếu các thuộc tính trong tương lai cần được thêm vào, ngay cả khi nó được chia thành các phương thức chia sẻ nhỏ hơn. Điều này cũng làm tăng sự khớp nối, giảm sự gắn kết và vi phạm nguyên tắc trách nhiệm duy nhất, mà ai đó trong nhóm của tôi đã chỉ ra.

Về cơ bản, rất nhiều sự trừu tượng hóa trong cơ sở mã này giúp giảm sự sao chép mã, nhưng nó làm cho việc mở rộng / thay đổi các phương thức trở nên khó khăn hơn khi chúng được thực hiện để có độ trừu tượng cao nhất. Tôi nên làm gì trong tình huống như thế này? Tôi là trung tâm của sự đổ lỗi, mặc dù những người khác không thể đồng ý về những gì họ cho là tốt, vì vậy cuối cùng nó làm tôi tổn thương.


10
thêm một mẫu mã để xóa mù chữ "" vấn đề "" sẽ giúp hiểu được tình hình nhiều hơn
Seabizkit

Tôi nghĩ rằng có hai nguyên tắc RẮN bị phá vỡ ở đây. Trách nhiệm duy nhất - nếu bạn chuyển Boolean cho một chức năng được cho là kiểm soát hành vi, chức năng đó sẽ không còn có trách nhiệm duy nhất. Một cái khác là nguyên tắc thay thế Liskov. Hãy tưởng tượng có một hàm lấy trong lớp A làm tham số. Nếu bạn vượt qua trong lớp B thay vì A, chức năng của chức năng đó có bị phá vỡ không?
bobek

Tôi nghi ngờ phương pháp A khá dài và làm nhiều việc. Có phải vậy không?
Rad80

Câu trả lời:


27

Nếu tôi cố gắng tạo một phương thức mới để xử lý B khác nhau, nó sẽ được gọi để sao chép mã.

Không phải tất cả các sao chép mã được tạo ra bằng nhau.

Giả sử bạn có một phương thức lấy hai tham số và cộng chúng lại với nhau được gọi total(). Nói rằng bạn có một cái khác được gọi add(). Việc thực hiện của họ trông hoàn toàn giống nhau. Họ có nên được hợp nhất thành một phương pháp? KHÔNG!!!

Nguyên tắc Đừng lặp lại chính mình hoặc DRY không phải là về việc lặp lại mã. Đó là về việc truyền bá một quyết định, một ý tưởng, xung quanh để nếu bạn thay đổi ý tưởng của mình, bạn phải viết lại ở mọi nơi bạn truyền bá ý tưởng đó xung quanh. Blegh. Đó là khủng khiếp. Đừng làm điều đó. Thay vào đó hãy sử dụng DRY để giúp bạn đưa ra quyết định ở một nơi .

Nguyên tắc DRY (Đừng lặp lại chính mình) nêu rõ:

Mỗi phần kiến ​​thức phải có một đại diện duy nhất, rõ ràng, có thẩm quyền trong một hệ thống.

wiki.c2.com - Đừng lặp lại chính mình

Nhưng DRY có thể bị biến thành thói quen quét mã tìm kiếm một triển khai tương tự có vẻ như đó là bản sao và dán của một nơi khác. Đây là dạng não chết của DRY. Chết tiệt, bạn có thể làm điều này với một công cụ phân tích tĩnh. Nó không giúp ích gì vì nó bỏ qua điểm DRY là giữ cho mã linh hoạt.

Nếu tổng số yêu cầu của tôi thay đổi, tôi có thể phải thay đổi totalviệc thực hiện. Điều đó không có nghĩa là tôi cần thay đổi addviệc thực hiện. Nếu một số goober đã đưa họ lại với nhau thành một phương pháp thì bây giờ tôi sẽ có một chút đau đớn không cần thiết.

Đau bao nhiêu? Chắc chắn tôi chỉ có thể sao chép mã và tạo một phương thức mới khi tôi cần. Vì vậy, không có vấn đề lớn phải không? Ác ma! Nếu không có gì khác bạn có giá cho tôi một cái tên tốt! Tên tốt rất khó để đến và không phản hồi tốt khi bạn hiểu ý nghĩa của chúng. Tên tốt, làm cho ý định rõ ràng, quan trọng hơn nguy cơ bạn đã sao chép một lỗi mà, thẳng thắn, dễ sửa hơn khi phương thức của bạn có tên đúng.

Vì vậy, lời khuyên của tôi là ngừng để phản ứng giật đầu gối với mã tương tự buộc cơ sở mã hóa của bạn thành nút thắt. Tôi không nói rằng bạn có thể bỏ qua thực tế là các phương thức tồn tại và thay vào đó sao chép và dán willy nilly. Không, mỗi phương pháp nên có một cái tên hay ho hỗ trợ cho một ý tưởng mà nó hướng tới. Nếu việc thực hiện của nó xảy ra để phù hợp với một số thực hiện ý tưởng tốt khác, ngay bây giờ, hôm nay, ai sẽ quan tâm?

Mặt khác, nếu bạn có một sum()phương thức có cách triển khai giống hệt hoặc thậm chí khác nhau total(), nhưng đến bao giờ, các yêu cầu tổng thể của bạn thay đổi thì bạn phải thay đổi sum(), rất có thể đây là những ý tưởng giống nhau dưới hai tên khác nhau. Mã không chỉ linh hoạt hơn nếu chúng được hợp nhất, nó sẽ ít gây nhầm lẫn hơn khi sử dụng.

Đối với các tham số Boolean, vâng, đó là một mùi mã khó chịu. Điều khiển không chỉ gây ra vấn đề, mà tệ hơn là nó cho thấy bạn đã cắt giảm một cách trừu tượng ở một điểm xấu. Trừu tượng được cho là để làm cho mọi thứ đơn giản hơn để sử dụng, không phức tạp hơn. Truyền bool vào một phương thức để kiểm soát hành vi của nó cũng giống như tạo một ngôn ngữ bí mật quyết định phương thức nào bạn thực sự gọi. Ow! Đừng làm thế với tôi. Đặt cho mỗi phương thức tên riêng của nó trừ khi bạn có một số trung thực để đa hình hóa đang diễn ra.

Bây giờ, bạn dường như bị kiệt sức về sự trừu tượng. Điều đó thật tệ vì sự trừu tượng là một điều tuyệt vời khi được thực hiện tốt. Bạn sử dụng nó rất nhiều mà không nghĩ về nó. Mỗi khi bạn lái xe mà không cần phải hiểu hệ thống giá đỡ và bánh răng, mỗi lần bạn sử dụng lệnh in mà không nghĩ đến việc ngắt hệ điều hành, và mỗi khi bạn đánh răng mà không nghĩ về từng chiếc lông.

Không, vấn đề bạn dường như đang đối mặt là sự trừu tượng xấu. Trừu tượng được tạo ra để phục vụ một mục đích khác với nhu cầu của bạn. Bạn cần các giao diện đơn giản thành các đối tượng phức tạp cho phép bạn yêu cầu các nhu cầu của bạn được đáp ứng mà không cần phải hiểu các đối tượng đó.

Khi bạn viết mã máy khách sử dụng một đối tượng khác, bạn sẽ biết nhu cầu của bạn là gì và bạn cần gì từ đối tượng đó. Nó không. Đó là lý do tại sao mã khách hàng sở hữu giao diện. Khi bạn là khách hàng, không có gì để cho bạn biết nhu cầu của bạn là gì ngoài bạn. Bạn đưa ra một giao diện cho thấy nhu cầu của bạn là gì và yêu cầu bất cứ thứ gì được trao cho bạn đều đáp ứng những nhu cầu đó.

Đó là sự trừu tượng. Khi khách hàng Tôi thậm chí không biết những gì tôi đang nói đến. Tôi chỉ biết những gì tôi cần từ nó. Nếu điều đó có nghĩa là bạn phải bọc thứ gì đó để thay đổi giao diện của nó trước khi giao nó cho tôi. Tôi không quan tâm. Chỉ cần làm những gì tôi cần làm. Dừng làm cho nó phức tạp.

Nếu tôi phải nhìn vào bên trong một sự trừu tượng để hiểu cách sử dụng nó thì sự trừu tượng đã thất bại. Tôi không cần phải biết nó hoạt động như thế nào. Chỉ là nó hoạt động. Đặt cho nó một cái tên hay và nếu tôi nhìn vào bên trong, tôi không nên ngạc nhiên với những gì tôi tìm thấy. Đừng bắt tôi phải nhìn vào bên trong để nhớ cách sử dụng nó.

Khi bạn nhấn mạnh rằng sự trừu tượng hoạt động theo cách này, số cấp độ đằng sau nó không thành vấn đề. Miễn là bạn không nhìn phía sau sự trừu tượng. Bạn đang khăng khăng rằng sự trừu tượng phù hợp với nhu cầu của bạn không thích ứng với nó. Để làm việc này, nó phải dễ sử dụng, có tên hay và không bị rò rỉ .

Đó là thái độ sinh ra Dependency Injection (hoặc chỉ tham khảo qua nếu bạn học cũ như tôi). Nó hoạt động tốt với thành phần ưa thích và ủy quyền hơn thừa kế . Thái độ đi bằng nhiều tên. Một trong những yêu thích của tôi là nói, đừng hỏi .

Tôi có thể nhấn chìm bạn trong các nguyên tắc cả ngày. Và có vẻ như đồng nghiệp của bạn đã có. Nhưng đây là điều: không giống như các lĩnh vực kỹ thuật khác, phần mềm này chưa đến 100 tuổi. Tất cả chúng ta vẫn đang tìm ra nó. Vì vậy, đừng để ai đó có nhiều cuốn sách nghe có vẻ đáng sợ học bắt nạt bạn viết khó đọc mã. Lắng nghe họ nhưng nhấn mạnh họ có ý nghĩa. Đừng lấy bất cứ điều gì trên đức tin. Những người viết mã theo cách nào đó chỉ vì họ được nói theo cách này mà không biết tại sao lại tạo ra những mớ hỗn độn lớn nhất.


Tôi hết lòng đồng ý. DRY là một từ viết tắt gồm ba chữ cái cho cụm từ bắt chữ ba từ Đừng lặp lại chính mình, lần lượt là một bài viết dài 14 trang trên wiki . Nếu tất cả những gì bạn làm là mù quáng lầm bầm ba chữ cái đó mà không đọc và hiểu bài viết 14 trang, bạn sẽ gặp rắc rối. Nó cũng liên quan chặt chẽ với Một lần và Chỉ một lần (OAOO) và liên quan nhiều hơn đến Điểm duy nhất (SPOT) / Nguồn duy nhất của sự thật (SSOT) .
Jörg W Mittag

"Việc triển khai của chúng trông hoàn toàn giống nhau. Chúng có nên được hợp nhất thành một phương thức không? KHÔNG !!!" - Điều ngược lại cũng đúng: chỉ vì hai đoạn mã khác nhau không có nghĩa là chúng không trùng lặp. Có một câu nói rất hay của Ron Jeffries trên trang wiki OAOO : "Tôi đã từng thấy Beck tuyên bố hai bản vá mã gần như hoàn toàn khác nhau là" trùng lặp ", thay đổi chúng sao cho chúng trùng lặp và sau đó loại bỏ trùng lặp mới được đưa vào với thứ gì đó rõ ràng tốt hơn. "
Jörg W Mittag

@ JörgWMittag tất nhiên. Điều cần thiết là ý tưởng. Nếu bạn đang sao chép ý tưởng với mã tìm khác nhau, bạn vẫn vi phạm.
candied_orange

Tôi phải tưởng tượng một bài viết dài 14 trang về việc không lặp lại chính mình sẽ có xu hướng lặp lại rất nhiều.
Chuck Adams

7

Câu nói thông thường mà tất cả chúng ta đều đọc ở đây và có:

Tất cả các vấn đề có thể được giải quyết bằng cách thêm một lớp trừu tượng.

Chà, điều này không đúng! Ví dụ của bạn cho thấy nó. Do đó, tôi đề xuất tuyên bố sửa đổi một chút (vui lòng sử dụng lại ;-)):

Mọi vấn đề đều có thể được giải quyết bằng cách sử dụng mức độ trừu tượng ĐÚNG.

Có hai vấn đề khác nhau trong trường hợp của bạn:

  • sự khái quát hóa quá mức gây ra bằng cách thêm mọi phương thức ở mức trừu tượng;
  • sự phân mảnh của các hành vi cụ thể dẫn đến ấn tượng không nhận được bức tranh lớn và cảm thấy bị mất. Một chút giống như trong một vòng lặp sự kiện windows.

Cả hai đều được đăng quang:

  • nếu bạn trừu tượng hóa một phương pháp trong đó mọi chuyên môn thực hiện nó khác nhau, mọi thứ đều ổn. Không ai có một vấn đề nắm bắt rằng một người Shapecó thể tính toán nó surface()theo một cách chuyên biệt.
  • Nếu bạn trừu tượng một số thao tác trong đó có một mẫu hành vi chung chung, bạn có hai lựa chọn:

    • hoặc bạn sẽ lặp lại hành vi phổ biến trong mọi chuyên ngành: điều này rất dư thừa; và khó bảo trì, đặc biệt là để đảm bảo rằng phần chung được xếp thẳng hàng trong các chuyên ngành:
    • bạn sử dụng một số loại biến thể của mẫu phương thức mẫu : điều này cho phép bạn tính đến hành vi phổ biến bằng cách sử dụng các phương thức trừu tượng bổ sung có thể dễ dàng chuyên biệt hóa. Nó ít dư thừa, nhưng các hành vi bổ sung có xu hướng trở nên cực kỳ chia rẽ. Quá nhiều sẽ có nghĩa là nó có lẽ quá trừu tượng.

Ngoài ra, phương pháp này có thể dẫn đến hiệu ứng ghép trừu tượng ở cấp thiết kế. Mỗi khi bạn muốn thêm một số loại hành vi chuyên biệt mới, bạn sẽ phải trừu tượng hóa nó, thay đổi cha mẹ trừu tượng và cập nhật tất cả các lớp khác. Đó không phải là kiểu truyền bá thay đổi mà người ta có thể mong muốn. Và nó không thực sự theo tinh thần trừu tượng không phụ thuộc vào chuyên môn hóa (ít nhất là trong thiết kế).

Tôi không biết thiết kế của bạn và không thể giúp nhiều hơn. Có lẽ nó thực sự là một vấn đề rất phức tạp và trừu tượng và không có cách nào tốt hơn. Nhưng tỷ lệ cược là gì? Các triệu chứng của quá mức phát triển là ở đây. Có thể đó là thời gian để xem xét lại nó, và xem xét thành phần trên khái quát ?


5

Bất cứ khi nào tôi thấy một phương thức trong đó hành vi chuyển sang loại tham số của nó, tôi ngay lập tức xem xét đầu tiên nếu phương thức đó thực sự thuộc về tham số phương thức. Ví dụ: thay vì có một phương thức như:

public void sort(List values) {
    if (values instanceof LinkedList) {
        // do efficient linked list sort
    } else { // ArrayList
        // do efficient array list sort
    }
}

Tôi sẽ làm điều này:

values.sort();

// ...

class ArrayList {
    public void sort() {
        // do efficient array list sort
    }
}

class LinkedList {
    public void sort() {
        // do efficient linked list sort
    }
}

Chúng tôi di chuyển hành vi đến nơi biết khi nào nên sử dụng nó. Chúng tôi tạo ra một sự trừu tượng thực sự nơi bạn không cần phải biết các loại hoặc các chi tiết của việc thực hiện. Đối với tình huống của bạn, có thể có ý nghĩa hơn khi di chuyển phương thức này từ lớp ban đầu (mà tôi sẽ gọi O) để nhập Avà ghi đè nó theo kiểu B. Nếu phương thức được gọi doIttrên một số đối tượng, di chuyển doItđến Avà ghi đè với hành vi khác nhau trong B. Nếu có các bit dữ liệu từ nơi doItđược gọi ban đầu hoặc nếu phương thức được sử dụng ở những nơi đủ, bạn có thể để phương thức ban đầu và ủy quyền:

class O {
    int x;
    int y;

    public void doIt(A a) {
        a.doIt(this.x, this.y);
    }
}

Chúng tôi có thể lặn sâu hơn một chút, mặc dù. Thay vào đó, hãy xem gợi ý sử dụng tham số boolean và xem những gì chúng ta có thể tìm hiểu về cách suy nghĩ của đồng nghiệp. Đề xuất của ông là để làm:

public void doIt(A a, boolean isTypeB) {
    if (isTypeB) {
        // do B stuff
    } else { 
        // do A stuff
    }
}

Điều này trông rất giống như instanceoftôi đã sử dụng trong ví dụ đầu tiên của mình, ngoại trừ việc chúng tôi đang thực hiện kiểm tra đó. Điều này có nghĩa là chúng ta sẽ phải gọi nó theo một trong hai cách:

o.doIt(a, a instanceof B);

hoặc là:

o.doIt(a, true); //or false

Theo cách thứ nhất, điểm gọi không biết Anó thuộc loại nào. Vì vậy, chúng ta có nên vượt qua booleans tất cả các cách? Đó thực sự là một mô hình mà chúng tôi muốn trên tất cả các cơ sở mã? Điều gì xảy ra nếu có một loại thứ ba chúng ta cần phải tính đến? Nếu đây là cách gọi phương thức, chúng ta nên di chuyển nó sang kiểu và để hệ thống chọn cách thực hiện cho chúng ta một cách đa hình.

Theo cách thứ hai, chúng ta phải biết loại atại điểm gọi. Thông thường điều đó có nghĩa là chúng ta hoặc đang tạo cá thể ở đó hoặc lấy một thể hiện của loại đó làm tham số. Tạo một phương thức trên Ođó sẽ có hiệu quả Bở đây. Trình biên dịch sẽ biết nên chọn phương pháp nào. Khi chúng ta đang lái xe qua những thay đổi như thế này, việc sao chép sẽ tốt hơn là tạo ra sự trừu tượng sai , ít nhất là cho đến khi chúng ta tìm ra nơi chúng ta thực sự sẽ đi. Tất nhiên, tôi đề nghị rằng chúng ta không thực sự hoàn thành cho dù chúng ta đã thay đổi đến thời điểm này.

Chúng ta cần xem xét kỹ hơn về mối quan hệ giữa AB. Nói chung, chúng ta được bảo rằng chúng ta nên ưu tiên thành phần hơn thừa kế . Điều này không đúng trong mọi trường hợp, nhưng nó đúng trong một số trường hợp đáng ngạc nhiên khi chúng ta khai thác. BKế thừa từ đó A, có nghĩa là chúng ta tin Blà một A. Bnên được sử dụng giống như A, ngoại trừ việc nó hoạt động hơi khác một chút. Nhưng những khác biệt đó là gì? Chúng ta có thể cho sự khác biệt một cái tên cụ thể hơn? Nó không phải Blà một A, nhưng thực sự Acó một Xcái có thể A'hay B'? Mã của chúng ta sẽ trông như thế nào nếu chúng ta làm điều đó?

Nếu chúng ta chuyển phương thức lên Anhư đã đề xuất trước đó, chúng ta có thể đưa một thể hiện Xvào Avà ủy thác phương thức đó cho X:

class A {
    X x;
    A(X x) {
        this.x = x;
    }

    public void doIt(int x, int y) {
        x.doIt(x, y);
    }
}

Chúng tôi có thể thực hiện A'B', và thoát khỏi B. Chúng tôi đã cải thiện mã bằng cách đặt tên cho một khái niệm có thể ẩn ý hơn và cho phép chúng tôi thiết lập hành vi đó trong thời gian chạy thay vì thời gian biên dịch. Athực sự đã trở nên ít trừu tượng hơn. Thay vì một mối quan hệ thừa kế mở rộng, nó đang gọi các phương thức trên một đối tượng được ủy quyền. Đối tượng đó là trừu tượng, nhưng chỉ tập trung hơn vào sự khác biệt trong thực hiện.

Có một điều cuối cùng để xem xét mặc dù. Hãy quay trở lại đề xuất của đồng nghiệp của bạn. Nếu tại tất cả các trang web cuộc gọi mà chúng tôi biết rõ loại hình Achúng tôi có, thì chúng tôi sẽ thực hiện các cuộc gọi như:

B b = new B();
o.doIt(b, true);

Chúng tôi giả định trước khi soạn mà AXnghĩa là một trong hai A'hoặc B'. Nhưng có lẽ ngay cả giả định này cũng không đúng. Đây có phải là nơi duy nhất mà sự khác biệt giữa ABvấn đề này? Nếu có, thì có lẽ chúng ta có thể có một cách tiếp cận hơi khác. Chúng tôi vẫn có một Xđó là một trong hai A'hoặc B', nhưng nó không thuộc về A. Chỉ O.doItquan tâm đến nó, vì vậy chúng ta chỉ chuyển nó cho O.doIt:

class O {
    int x;
    int y;

    public void doIt(A a, X x) {
        x.doIt(a, x, y);
    }
}

Bây giờ trang web cuộc gọi của chúng tôi trông như:

A a = new A();
o.doIt(a, new B'());

Một lần nữa, Bbiến mất, và sự trừu tượng di chuyển vào tập trung hơn X. Lần này, mặc dù, Athậm chí còn đơn giản hơn bằng cách biết ít hơn. Nó thậm chí còn ít trừu tượng hơn.

Điều quan trọng là giảm sự trùng lặp trong một cơ sở mã, nhưng chúng ta phải xem xét tại sao sự trùng lặp xảy ra ở nơi đầu tiên. Sao chép có thể là một dấu hiệu trừu tượng sâu hơn đang cố gắng thoát ra.


1
Tôi nhận ra rằng ví dụ mã "xấu" mà bạn đang đưa ra ở đây tương tự như mã tôi sẽ làm theo ngôn ngữ không phải là OO. Tôi tự hỏi liệu họ có học được những bài học sai và đưa họ vào thế giới OO như cách họ viết mã không?
Baldrickk

1
@Baldrickk Mỗi mô hình mang đến những cách suy nghĩ riêng, với những ưu điểm và nhược điểm riêng. Trong Haskell chức năng, khớp mẫu sẽ là cách tiếp cận tốt hơn. Mặc dù trong một ngôn ngữ như vậy, một số khía cạnh của vấn đề ban đầu cũng không thể xảy ra.
cbojar

1
Đây là câu trả lời chính xác. Một phương thức thay đổi triển khai dựa trên loại mà nó hoạt động phải là một phương thức của loại đó.
Roman Reiner

0

Trừu tượng bởi sự kế thừa có thể trở nên khá xấu xí. Phân cấp lớp song song với các nhà máy điển hình. Tái cấu trúc có thể trở thành một nhức đầu. Và cũng phát triển sau này, nơi bạn đang ở.

Tồn tại một giải pháp thay thế: điểm mở rộng , trừu tượng nghiêm ngặt và tùy chỉnh theo từng cấp. Nói một tùy chỉnh của khách hàng chính phủ, dựa trên tùy chỉnh đó cho một thành phố cụ thể.

Một cảnh báo: Thật không may, điều này hoạt động tốt nhất khi tất cả (hoặc hầu hết) các lớp được thực hiện mở rộng. Không có lựa chọn cho bạn, có thể trong nhỏ.

Khả năng mở rộng này hoạt động bằng cách có một lớp cơ sở đối tượng có thể mở rộng chứa các phần mở rộng:

void f(CreditorBO creditor) {
    creditor.as(AllowedCreditorBO.class).ifPresent(allowedCreditor -> ...);
}

Trong nội bộ có một ánh xạ lười biếng của đối tượng đến các đối tượng mở rộng theo lớp mở rộng.

Đối với các lớp và các thành phần GUI có cùng khả năng mở rộng, một phần có tính kế thừa. Thêm các nút và như vậy.

Trong trường hợp của bạn, việc xác thực sẽ xem xét liệu nó có được mở rộng hay không và xác thực chính nó đối với các phần mở rộng. Giới thiệu các điểm mở rộng chỉ cho một trường hợp thêm mã không thể hiểu được, không tốt.

Vì vậy, không có giải pháp nào ngoài việc cố gắng làm việc trong bối cảnh hiện tại.


0

Control kiểm soát dòng chảy ẩn 'nghe có vẻ quá tay đối với tôi.
Bất kỳ cấu trúc hoặc phần tử được đưa ra khỏi bối cảnh có thể có đặc điểm đó.

Trừu tượng là tốt. Tôi tiết chế chúng với hai nguyên tắc:

  • Tốt hơn đừng trừu tượng quá sớm. Đợi thêm ví dụ về các mẫu trước khi trừu tượng hóa. 'Thêm' tất nhiên là chủ quan và cụ thể cho tình huống khó khăn.

  • Tránh quá nhiều mức độ trừu tượng chỉ vì sự trừu tượng là tốt. Một lập trình viên sẽ phải giữ các cấp độ đó trong đầu để tìm mã mới hoặc thay đổi khi họ làm giảm mã cơ sở và đi sâu 12 cấp. Mong muốn về mã được trừu tượng hóa tốt có thể dẫn đến nhiều cấp độ mà nhiều người khó theo dõi. Điều này cũng dẫn đến các cơ sở mã hóa 'ninja chỉ duy trì'.

Trong cả hai trường hợp, 'nhiều hơn và' quá nhiều 'không phải là số cố định. Nó phụ thuộc. Đó là những gì làm cho nó khó khăn.

Tôi cũng thích bài viết này từ Sandi Metz

https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction

Sao chép rẻ hơn nhiều so với trừu tượng sai

thích sao chép hơn trừu tượng sai

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.