2015-06-11 14 views
14

Cho phép nói rằng chúng tôi có lớp A trong gói A và hạng B trong gói B. Nếu đối tượng của lớp A có tham chiếu đến lớp B, thì hai lớp được cho là có sự liên kết giữa chúng.Việc tách hai lớp ở mức giao diện có nghĩa là gì?

Để giải quyết khớp nối, bạn nên xác định giao diện trong gói A được thực hiện bởi lớp trong gói B. Sau đó, đối tượng của lớp A có thể tham chiếu đến giao diện trong gói A. Đây thường là một ví dụ trong "đảo ngược sự phụ thuộc".

Đây có phải là ví dụ về "tách hai lớp ở cấp độ giao diện" hay không. Nếu có, làm thế nào nó loại bỏ các khớp nối giữa các lớp và giữ lại các chức năng tương tự khi hai lớp được kết hợp?

+2

Tôi đánh giá cao các ấn phẩm của Robert C. Martin về các quy tắc SOLID về lập trình hướng đối tượng. Dưới đây là một số chi tiết về vấn đề của bạn: http://www.objectmentor.com/resources/articles/dip.pdf –

Trả lời

29

Hãy để chúng tôi tạo một ví dụ hư cấu.

Lớp A trong gói packageA:

package packageA; 

import packageB.B; 

public class A { 
    B myB; 

    public A() { 
     this.myB = new B(); 
    } 

    public void doSomethingThatUsesB() { 
     System.out.println("Doing things with myB"); 
     this.myB.doSomething(); 
    } 
} 

Lớp B trong gói packageB:

package packageB; 

public class B { 
    public void doSomething() { 
     System.out.println("B did something."); 
    } 
} 

Như bạn thấy, A phụ thuộc vào B. Không thể sử dụng B, A. Nhưng nếu chúng ta muốn thay thế B trong tương lai bằng một số BetterB thì sao? Bây giờ, chúng ta bắt đầu để tạo ra một giao diện Inter trong packageA:

package packageA; 

public interface Inter { 
    public void doSomething(); 
} 

Để sử dụng giao diện này, chúng tôi import packageA.Inter; và để B implements Inter trong B và thay thế tất cả các lần xuất hiện của B trong A với Inter. Kết quả là phiên bản này được sửa đổi của A:

package packageA; 

public class A { 
    Inter myInter; 

    public A() { 
     this.myInter = ???; // What to do here? 
    } 

    public void doSomethingThatUsesInter() { 
     System.out.println("Doing things with myInter"); 
     this.myInter.doSomething(); 
    } 
} 

Tại thời điểm này, chúng ta thấy đã có sự phụ thuộc A-B đã biến mất: các import packageB.B; không còn cần thiết. Chỉ có một vấn đề: chúng ta không thể khởi tạo một thể hiện của một giao diện. Nhưng Inversion of control đến để giải cứu. Thay vì instantiating một cái gì đó kiểu Inter wihtin constructor A 's, chúng tôi sẽ yêu cầu cái gì mà implements Inter như tham số cho các nhà xây dựng:

package packageA; 

public class A { 
    Inter myInter; 

    public A(Inter myInter) { 
     this.myInter = myInter; 
    } 

    public void doSomethingThatUsesInter() { 
     System.out.println("Doing things with myInter"); 
     this.myInter.doSomething(); 
    } 
} 

Với cách tiếp cận này, chúng tôi bây giờ có thể thay đổi việc thực hiện cụ thể của Inter trong A theo ý thích. Giả sử bạn viết một lớp mới BetterB:

package packageB; 

import packageA.Inter; 

public class BetterB implements Inter { 
    @Override 
    public void doSomething() { 
     System.out.println("BetterB did something."); 
    } 
} 

Bây giờ chúng ta có thể instantiante A s với Inter triển khai khác nhau:

Inter b = new B(); 
A aWithB = new A(b); 
aWithB.doSomethingThatUsesInter(); 

Inter betterB = new BetterB(); 
A aWithBetterB = new A(betterB); 
aWithBetterB.doSomethingThatUsesInter(); 

Và chúng tôi không cần phải thay đổi bất cứ điều gì trong A. Mã này hiện đã được tách riêng và bạn có thể thay đổi việc triển khai cụ thể Inter theo ý muốn, miễn là (các) hợp đồng của Inter được (hài lòng). Đáng chú ý nhất, bạn có thể hỗ trợ mã, sẽ được tạo ra trong tương lai và thực hiện Inter.

+1

+1 Inversión kiểm soát đến để giải cứu. Đây là sự tách rời và đảo ngược tốt nhất của quan hệ kiểm soát mà tôi từng thấy. Và tôi đã đọc sách về chủ đề này. –

+0

Rất tốt giải thích ..... làm cho khái niệm rõ ràng hơn cho tôi –

+0

Nhưng không phải lớp A bây giờ phụ thuộc vào giao diện? Bạn chỉ cần tách nó ra khỏi B và ghép nó với giao diện phải không? Làm thế nào là tốt hơn? – 11m0

3

Tình huống bạn mô tả loại bỏ sự phụ thuộc mà lớp A có về triển khai cụ thể của lớp B và thay thế bằng giao diện. Bây giờ lớp A có thể chấp nhận bất kỳ đối tượng nào thuộc loại thực hiện giao diện, thay vì chỉ chấp nhận lớp B. Thiết kế vẫn giữ nguyên chức năng tương tự vì lớp B được thực hiện để triển khai giao diện đó.

4

Hãy tưởng tượng rằng chức năng của B là ghi nhật ký vào một số cơ sở dữ liệu. Lớp B phụ thuộc vào chức năng của lớp DB và cung cấp một số giao diện cho chức năng ghi nhật ký của nó cho các lớp khác.

Lớp A cần các chức năng khai thác gỗ của B, nhưng là không quan tâm, nơi các bản ghi được ghi vào. Nó không quan tâm đến DB, nhưng vì nó phụ thuộc vào B, nó cũng phụ thuộc vào DB. Điều này không phải là rất mong muốn.

Vì vậy, những gì bạn có thể làm, là để phân chia các lớp B thành hai lớp: Một lớp trừu tượng L mô tả các chức năng khai thác gỗ (và không phụ thuộc vào DB), và thực hiện tùy thuộc vào DB.

Sau đó, bạn có thể tách lớp A khỏi B, bởi vì bây giờ A sẽ chỉ phụ thuộc vào L. B bây giờ cũng phụ thuộc vào L, đó là lý do tại sao nó được gọi là đảo ngược phụ thuộc, bởi vì B cung cấp các chức năng được cung cấp trong L.

Kể từ A bây giờ phụ thuộc vào chỉ một nạc L, bạn có thể dễ dàng sử dụng nó với cơ chế khai thác gỗ khác, không phụ thuộc vào DB. Ví dụ. bạn có thể tạo một logger dựa trên console đơn giản, thực hiện giao diện được định nghĩa trong L.

Nhưng kể từ bây giờ A không phụ thuộc vào B nhưng (trong nguồn) chỉ trên giao diện trừu tượng L lúc chạy nó phải được thiết lập để sử dụng một số thực hiện cụ thể của L (B ví dụ). Vì vậy, cần phải có ai đó khác nói với A để sử dụng B (hoặc cái gì khác) trong thời gian chạy. Và điều đó được gọi là Inversion of Control, bởi vì trước A quyết định sử dụng B, nhưng bây giờ người khác (ví dụ một container) cho A sử dụng B trong thời gian chạy.

Các vấn đề liên quan