2009-12-14 8 views
11

Tôi có một cấu trúc lớp mà tôi muốn một số phương thức trong một lớp cơ sở có thể truy cập được từ các lớp được lấy trực tiếp từ lớp cơ sở, nhưng không phải các lớp bắt nguồn từ các lớp dẫn xuất. Theo đặc tả Java Language, có thể ghi đè các đặc tả truy cập trên các phương thức kế thừa để làm cho chúng trở nên công khai hơn, nhưng không phải riêng tư hơn. Ví dụ: đây là ý chính của những gì tôi cần làm, nhưng là bất hợp pháp:Có thể ẩn hoặc giảm quyền truy cập vào Phương thức kế thừa trong Java không?

// Defines myMethod 
public class Base { 
    protected void myMethod() {} 
} 

// Uses myMethod and then hides it. 
public class DerivedOne extends Base { 
    @Override 
    private void myMethod(); 
} 

// can't access myMethod. 
public class DerivedTwo extends DerivedOne { 

} 

Có cách nào để thực hiện việc này không?

Được chỉnh sửa để giải thích lý do tôi muốn thực hiện việc này:

Trong trường hợp này cấu trúc lớp là cấu trúc xử lý và nhập dữ liệu. Nó đọc và phân tích các tệp văn bản đầy đủ dữ liệu dạng bảng và sau đó lưu trữ chúng trong cơ sở dữ liệu.

Lớp cơ sở là lớp bảng cơ sở quản lý phần xử lý cơ sở dữ liệu của nó. Có một số lượng khá lớn các chức năng chứa trong đó là điều phổ biến đối với tất cả các loại bảng - như khi chúng ở trong cơ sở dữ liệu, chúng trở nên đồng nhất.

Lớp trung lưu dành riêng cho loại bảng trong tệp đang được phân tích cú pháp và có phân tích bảng và logic nhập. Nó cần truy cập vào một số hàm truy cập cơ sở dữ liệu của lớp cơ sở.

Lớp cấp cao nhất dành riêng cho bảng và không có gì khác ngoài việc khởi tạo bố cục của bảng theo cách mà lớp cha mẹ có thể hiểu. Ngoài ra người dùng của lớp cơ sở không cần phải xem hoặc truy cập các hàm cơ sở dữ liệu cụ thể mà lớp trung lưu làm. Về bản chất, tôi muốn tiết lộ các chức năng này chỉ ở một cấp trên lớp cơ sở và không có ai khác.

Tôi hỏi vì, mặc dù mã tôi đăng dưới dạng ví dụ là bất hợp pháp, có thể có một số phương tiện khác để thực hiện cùng một kết thúc. Tôi hỏi nếu có.

Có lẽ ẩn là cách sai để cụm từ này - những gì tôi thực sự cần làm là phơi bày một số chức năng cần được riêng tư cho lớp cơ sở đến lớp một cấp trong hệ thống phân cấp. Ẩn sẽ thực hiện điều này - nhưng tôi có thể thấy cách ẩn náu sẽ là một vấn đề. Có một cách khác để làm điều này?

+2

Kể từ DerivedOne là một Base, nó phải chứa đến hợp đồng cơ sở của. Điều này bằng cách này, là lý do tại sao phái đoàn (mô hình hỗn hợp) thường được ưa thích hơn thừa kế. –

+0

@Steve Tôi không tin mẫu ghép sẽ hoạt động ở đây. Về bản chất tôi chỉ cần tiết lộ một số chức năng mà nên được tư nhân cho lớp cơ sở đến một lớp đó là một cấp lên. –

+0

Đã cập nhật câu trả lời của tôi. – rsp

Trả lời

15

Tôi nghĩ bản chất của vấn đề như bạn đã đặt ra cho thấy vấn đề khái niệm với mô hình đối tượng của bạn. Bạn đang cố gắng để mô tả các trách nhiệm riêng biệt khác nhau như là "là một" mối quan hệ khi thực sự những gì bạn nên làm là mô tả "có một" hoặc "sử dụng một" mối quan hệ. Thực tế là bạn muốn ẩn chức năng lớp cơ sở từ một lớp con cho tôi biết vấn đề này không thực sự ánh xạ lên cây thừa kế ba tầng.

Có vẻ như bạn đang mô tả một vấn đề ORM cổ điển. Chúng ta hãy xem xét điều này một lần nữa và xem liệu chúng ta có thể tái lập bản đồ nó vào khái niệm khác hơn là nghiêm ngặt "là một" kế thừa, bởi vì tôi thực sự nghĩ rằng vấn đề của bạn không phải là kỹ thuật, đó là khái niệm:

Bạn nói:

Lớp cơ sở là lớp bảng cơ sở quản lý phần xử lý cơ sở dữ liệu là . Có một số tiền hợp lý của chức năng chứa trong đó là chung cho tất cả các loại bảng - như một lần chúng nằm trong cơ sở dữ liệu mà chúng trở thành đồng nhất .

Điều này có thể rõ ràng hơn, nhưng có vẻ như chúng tôi có một lớp học cần quản lý kết nối DB và các hoạt động db thông thường. Theo dõi Single Responsibility, tôi nghĩ chúng ta đã hoàn thành ở đây. Bạn không cần phải mở rộng lớp này, bạn cần phải tay nó vào một lớp học cần sử dụng chức năng của nó.

Tầng lớp trung lưu là cụ thể cho loại bảng trong file là phân tích cú pháp, và có phân tích bảng và lý nhập khẩu. Nó cần quyền truy cập vào một số truy cập cơ sở dữ liệu của lớp cơ sở chức năng.

"Lớp trung lưu" ở đây có vẻ hơi giống như Data Mapper. Lớp này không cần phải mở rộng lớp trước đó, nó cần phải sở hữu một tham chiếu đến nó, có thể được tiêm vào hàm khởi tạo hoặc trình thiết lập làm giao diện.

Lớp cấp cao nhất là cụ thể cho bảng và không gì hơn là khởi bố trí của bảng một cách các lớp cha mẹ có thể hiểu được. Ngoài ra người dùng của lớp cơ sở không cần xem hoặc truy cập cơ sở dữ liệu các chức năng cụ thể mà lớp trung gian thực hiện.Về bản chất, tôi muốn tiết lộ các chức năng này chỉ ở một cấp phía trên lớp cơ sở và không có ai khác.

Tôi không rõ lý do tại sao một lớp cao cấp dường như có kiến ​​thức về lược đồ db (ít nhất đó là cụm từ "khởi tạo bố cục của bảng"), nhưng một lần nữa, nếu mối quan hệ giữa hai lớp đầu tiên là encapsulation ("has a"/"uses a") instead of inheritance ("is a"), tôi không nghĩ đây là vấn đề.

+0

Bạn mang lại một số điểm tốt, và nó chắc chắn mang lại cho tôi một cái gì đó để suy nghĩ về. Vấn đề tôi gặp phải là tình huống mà tôi đang đối phó không cho chính nó đóng gói quá nhiều. Tôi sợ tôi không thể cung cấp thêm thông tin - đó là công việc và sở hữu độc quyền và tôi thực sự hơi lo lắng về số tiền tôi đã đưa ra. Tuy nhiên, tôi đánh giá cao những hiểu biết của bạn và tôi sẽ xem xét kỹ cấu trúc của tôi. –

+1

Thực ra bây giờ tôi đã nhìn lại cấu trúc của mình, có vẻ như câu hỏi của tôi là trong thực tế sinh ra từ thứ hai-đầu-buổi chiều-giấc ngủ-thiếu-não-thất bại và không đủ caffeine. Khi nó quay ra phương pháp tôi muốn ẩn từ các tầng lớp cao hơn được dự định để được hiển thị cho họ trong trường hợp họ cần phải ghi đè lên nó. Tôi không biết tại sao tôi lại nghĩ khác. Trong mọi trường hợp, quy tắc lưu ý tốt để ghi nhớ cho các thiết kế trong tương lai. Nếu bao giờ nghĩ rằng việc ẩn là cần thiết - thiết kế của người đó là sai. Nhìn vào đóng gói. Cảm ơn câu trả lời chu đáo và chi tiết. –

10

số tôi không chắc chắn lý do tại sao bạn muốn trích dẫn spec và sau đó yêu cầu nếu có cách nào để làm ngược lại những gì mà spec nói ...

Có lẽ nếu bạn giải thích lý do tại sao bạn muốn để làm điều này, bạn có thể nhận được một số đề xuất trên cách.

+0

Được rồi, ẩn là cách sai để giải thích điều này. Mặc dù điều đó sẽ đạt được mục đích mà tôi cần. Một cách tốt hơn để giải thích điều này là từ hướng khác: tôi cần để lộ một số chức năng riêng tư cho lớp cơ sở cho lớp xuất phát trực tiếp từ nó - nhưng không phải đến các lớp ở trên đó. –

6

Khi ghi đè phương thức, bạn chỉ có thể làm cho nó thêm công khai, không riêng tư hơn. Tôi không biết lý do tại sao bạn sử dụng từ "chung"

Hãy nhớ rằng, đặt hàng từ tối thiểu để hạn chế nhất:

public<protected<default<private 

Vâng, "protected" là một modifier truy cập ít hạn chế hơn default (khi không có sửa đổi được sử dụng), vì vậy bạn có thể ghi đè lên một phương pháp mặc định đánh dấu phương pháp ghi đè là protected, nhưng không làm ngược lại.

Có thể: Bạn có thể ghi đè phương thức protected với public một.

Không thể: Bạn không thể ghi đè phương thức public với protected một.

+0

Bạn có thể ghi đè lên 'bảo vệ' bằng' được bảo vệ' trong một gói khác, điều này thật kỳ lạ. Ah, 'bảo vệ' là "lạ". –

+0

Tất nhiên, bạn luôn có thể sử dụng cùng một công cụ sửa đổi truy cập để ghi đè. Alcon hỏi về việc thay đổi cấp độ truy cập. – andandandand

+0

Cố định từ "chung";) Khi đầu của tôi là trong các từ mã không phải là sức mạnh của tôi. Tôi biết quy tắc này - tôi hỏi liệu có cách nào để giải quyết vấn đề này không. –

6

Nếu bạn đã làm điều này thì DerivedOne sẽ không phải là Cơ sở, từ quan điểm của DerivedTwo. Thay vào đó những gì bạn muốn là một lớp wrapper

//Uses myMethod but keeps it hidden 
public class HiddenBase { 
    private final Base base = new Base(); 
    private void myMethod(); 
    public void otherMethod() {base.otherMethod();} 
} 

Bạn không thể truy cập các phương pháp bảo vệ của các cơ sở mặc dù theo cách này ...

+0

Chỉ có 'cơ sở riêng cuối cùng Base base = new Base();'. –

+0

Ah yeah, cảm ơn, tôi sẽ sửa lỗi đó. – KernelJ

2

gì bạn mô tả đi kèm chặt chẽ với những gì lớp protected truy cập là, xuất phát các lớp học có thể truy cập, tất cả các lớp khác đều không thể. Nếu bạn thừa kế từ các lớp cơ sở mà bạn không có quyền kiểm soát điều này có thể gây ra vấn đề, bạn có thể làm cho phương thức không thể truy cập được với người khác bằng cách ném một ngoại lệ trong khi làm cho mã được thừa kế có sẵn cho các lớp của bạn bằng cách gọi siêu trực tiếp, như sau:

// Uses myMethod and then hides it. 
public class DerivedOne extends Base { 
    @Override 
    public void myMethod() { 
     throw new IllegalStateException("Illegal access to myMethod"); 
    } 

    private void myPrivateMethod() { 
     super.myMethod(); 
    } 

} 

Sửa: để trả lời xây dựng của bạn, nếu tôi hiểu bạn một cách chính xác bạn cần phải xác định hành vi trong bối cảnh của lớp cơ sở được định nghĩa trong tầng lớp trung lưu. Các phương thức được bảo vệ trừu tượng sẽ không được ẩn đối với các lớp bắt nguồn từ tầng lớp trung lưu.

Một cách tiếp cận có thể là xác định giao diện với các phương thức bạn cần trừu tượng trong lớp cơ sở, giữ tham chiếu cuối cùng riêng tư trong lớp cơ sở và cung cấp tham chiếu đến việc triển khai khi xây dựng các đối tượng lớp trung lưu.

Giao diện sẽ được triển khai trong một lớp (tĩnh?) Được lồng trong lớp trung lưu. Ý tôi là gì:

public interface Specific { 
    public void doSomething(); 
} 

public class Base { 
    private final Specific specificImpl; 

    protected Base(Specific s) { 
     specificImpl = s; 
    } 

    public void doAlot() { 

     // ... 

     specificImpl.doSomething(); 

     // ... 
    } 
} 

public class Middle extends Base { 

    public Middle() { 
     super(new Impl()); 
    } 

    private Impl implements Specific { 

     public void doSomething() { 

      System.out.println("something done"); 
     } 
    } 
} 

public class Derived extends Middle { 

    // Access to doAlot() 
    // No access to doSomething() 
} 
+0

Hmm ... thật xấu. Yeah, bảo vệ đến gần, nhưng không hoàn toàn ở đó. Ý tưởng tốt mặc dù. Đi cho chỉ không nhìn thấy được. Nếu tôi phải giải quyết việc bảo vệ một người hướng dẫn, hãy bỏ qua chúng thì cũng tốt. Đã được kinda hy vọng có một cách cho một tiết lộ giới hạn của các chức năng, chỉ vì lợi ích của sạch sẽ. –

3

Thừa kế hoạt động vì ở mọi nơi bạn có thể sử dụng lớp cơ sở, bạn cũng có thể sử dụng một trong các lớp con của nó. Hành vi có thể khác, nhưng API thì không. Khái niệm này được gọi là the Liskov substitution principle.

Nếu bạn có thể hạn chế quyền truy cập vào phương thức, lớp kết quả sẽ không có cùng API và bạn sẽ không thể sử dụng thay thế một thể hiện của lớp cơ sở cho một trong các lớp dẫn xuất, phủ nhận lợi thế của thừa kế .

gì bạn thực sự muốn đạt được có thể được thực hiện với giao diện:

interface IBase1 { 
} 

class Derived1 implements IBase1 { 
    public void myMethod() { 
    } 
} 

class Derived2 implements IBase1 { 
} 

class UseMe { 
    public void foo(IBase1 something) { 
    // Can take both Derived1 and Derived2 
    // Can not call something.myMethod() 
    } 
    public void foo(Derived1 something) { 
    something.myMethod(); 
    } 
    public void foo(Derived2 something) { 
    // not something.myMethod() 
    } 
} 
+0

Giao diện sẽ không thực hiện điều này vì có chức năng, không chỉ là một giao diện, thông thường cho tất cả được viết vào lớp cơ sở. Xem bản chỉnh sửa để có giải thích tốt hơn về những gì tôi thực sự đang cố gắng thực hiện. –

2

Có thể, nhưng đòi hỏi một chút thao tác gói và có thể dẫn đến một cấu trúc phức tạp hơn một chút so với quý vị muốn để làm việc với trên đường dài.

xem xét như sau:


package a; 

public class Base { 
    void myMethod() { 
     System.out.println("a"); 
    } 
} 

package a; 

public class DerivedOne extends Base { 
    @Override 
    void myMethod() { 
     System.out.println("b"); 
    } 
} 

package b; 

public class DerivedTwo extends a.DerivedOne { 
    public static void main(String... args) { 
     myMethod(); // this does not compile... 
    } 
} 

Tôi muốn giới thiệu là tốt đẹp để bản thân, đồng nghiệp của bạn và bất kỳ người nào khác mà kết thúc phải duy trì mã của bạn; suy nghĩ lại các lớp và giao diện của bạn để tránh điều này.

1

bạn phải thực hiện phương pháp cuối cùng khi ghi đè lên nó

public class Base { 
protected void myMethod() {} 
} 

// Uses myMethod and then hides it. 
public class DerivedOne extends Base { 
@Override 
final protected void myMethod(); //make the method final 
} 


public class DerivedTwo extends DerivedOne { 
    // can't access myMethod here. 
}