OOD: Kế thừa Java và truy cập vào các phương thức con thông qua việc truyền


8

Tôi có một vài lớp ParentChild1... được Child9triển khai trong Java. Parentlà một lớp trừu tượng, chứa tất cả các biến chung của các lớp con (rất nhiều, đó là lý do chính tại sao tôi tạo Parentmột lớp trừu tượng chứ không phải giao diện), một số phương thức trừu tượng và một số phương thức được triển khai.

Một số lớp con có các phương thức tùy chỉnh dành riêng cho chúng. Vì vậy, tôi thường thấy mình gọi một phương thức con bằng cách sử dụng downcasting:

Parent p = new Child1();
((Child1) p).child1SpecificMethod();

Bằng cách nào đó tôi có cảm giác rằng đây là một thực tế tồi tệ, nhưng tôi không chắc đó có phải là trường hợp tương ứng làm thế nào để cải thiện thiết kế hay không.

- Chỉnh sửa - Dù sao thì điều tôi có thể nên thay đổi là việc tôi sử dụng lớp Parent để tổ chức nhiều biến chung (hiện tại), biến chúng (hoặc một đối tượng chứa) thành viên của các lớp cụ thể.


6
Nếu bạn biết, giá trị của p thuộc loại Child1, tại sao không tạo p thuộc loại Child1, do đó không cần phải truyền?
Benni

2
Nói chung là có, đây là thực hành xấu. Nếu bạn thấy mình thường làm phiền mọi thứ, thì có lẽ bạn đã làm sai điều gì đó. Tuy nhiên, thật khó để nói nhiều hơn mà không có thêm thông tin về tình huống của bạn.
Manila Cohn

4
Tại sao bạn tuyên bố plà một Parentở nơi đầu tiên? Bám sát các loại chung là cách thực hành tốt cho API và các loại trả về, nhưng không nằm trong cùng phạm vi mà bạn tự phân bổ một lớp cụ thể.
Kilian Foth

Có vẻ như bạn đang thừa kế quyền thừa kế trong trường hợp sử dụng của bạn. Nếu con cái không thể sử dụng quyền thừa kế để lợi dụng cha mẹ chung, bạn sẽ cần xem lại thiết kế lớp học của mình
kolossus

2
có thể trùng lặp làm thế nào để tránh downcasting?
gnat

Câu trả lời:


11

Đây không chỉ là thực hành xấu , đây là phức tạp không cần thiết.

Tại sao bạn sử dụng thừa kế nói chung?

Khi bạn sử dụng tính kế thừa, bạn có một tập hợp hành vi chung mà bạn muốn cung cấp cho nhiều nhà mạng khác nhau. Điều này bao gồm kế thừa lớp cũng như kế thừa giao diện als . Người thừa kế , có thể nói, đôi khi là một chuyên môn của giai cấp mà nó được thừa hưởng; điều này chủ yếu đúng với sự kế thừa giai cấp.

Hãy nghĩ về một lớp xe và một lớp con porsche (điển hình là một -relationship). Bạn có hành vi chung như khởi động / dừng động cơ , lái và như vậy. Nếu bạn đối xử với một porsche như một chiếc xe hơi, bạn đang bị ràng buộc để khía cạnh này của hành vi của nó. Nếu bạn đã biết, rằng bạn chỉ muốn có một porsche và chỉ đối phó với nó như một porsche , nó là không cần thiết để instantiiate một porsche như một chiếc xe và get porsche-hành vi thông qua đúc .

Đa hình có ý nghĩa theo cách khác:

Bạn có một chiếc pô và cần xử lý nó từ khía cạnh của một chiếc xe hơi; ví dụ lái xe

Miễn là của bạn chấp nhận lái trái , lái phải , tăng lên , chuyển xuống , v.v. bạn có thể sử dụng đa hình / thay thế cho cái khác.

Nó là tốt hơn, để khởi tạo các đối tượng của bạn ở dạng chuyên biệt của họ. Sau đó, bạn có thể tận dụng tối đa tính đa hình và chỉ sử dụng nó khi bạn cần .

Điều đó nói rằng: Parent p = new Child1();không có ý nghĩa đối với tôi.

Chỉnh sửa: Tôi sẽ triển khai pogio khác nhau (thông qua thành phần ), nhưng vì lợi ích của ví dụ, đó là một chiếc xe hơi.


4

Cảm giác của bạn là đúng để coi đó là một thực hành xấu. Hãy tưởng tượng mã ví dụ của bạn hơi khác một chút:

Parent p = createObject();
((Child1) p).child1SpecificMethod();

Làm thế nào để bạn biết, giá trị của p thực sự là Child1? Bạn không và điều này có thể gây ra ClassCastException khi chạy. Nếu bạn cần gọi child1SpecificMethod () trong mã của mình, bạn phải đảm bảo rằng p thuộc loại Child1. Nếu điều đó là không thể vì Object p được truyền vào mã của bạn (ví dụ như tham số phương thức) dưới dạng kiểu Parent, bạn có thể xem xét sử dụng một biến thể của Kiểu khách truy cập và thực thi child1SpecificMethod trong phương thức xử lý của đối tượng khách truy cập của bạn, xử lý Con1.


"bạn phải chắc chắn rằng p thuộc kiểu Child1" - điều đó thực sự không thể xảy ra vì Object p được truyền dưới dạng tham số phương thức. Các mẫu khách truy cập là gợi ý tốt.
jpmath

4

Sử dụng tra cứu các khả năng thay thế. Không có quyền truy cập vào các lớp con, xem xét chúng triển khai lớp Cha mẹ.

Sau đó xác định giao diện chỉ định một số khả năng , tính năng.

interface Child1Specific {
    void child1SpecificMethod();
}

Sử dụng:

Parent parent = ...
Child1Specific specific = parent.lookup(Child1Specific.class);
if (specific1 != null) {
    specific1.child1SpecificMethod();
}

Cơ chế khám phá này rất linh hoạt. Sử dụng ủy quyền thay vì thừa kế có thể khá bổ ích. Lưu ý, có các lớp con không còn cần thiết.

Hoặc trong java 8 (trong đó có thể có một số biến thể và giao diện cũng có thể hoạt động):

Optional<Child1Specific> specific = parent.lookup(Child1Specific.class);
if (specific1.isPresent()) {
    specific1.get().child1SpecificMethod();
}

Làm cho lớp Parent tìm kiếm một số khả năng:

public class Parent {
    protected final Map<Class<?>, Object> capabilities = new HashMap<>();
    protected final <T> void registerCapability(Class<T> klass, T object);

    public <T> T lookup(Class<T> klass) {
        Object object = capabilities.get(klass);
        return object == null ? null : klass.cast(object);
    }

Hoặc trong java 8:

    public <T> Optional<T> lookup(Class<T> klass) {
        Object object = capabilities.get(klass);
        return Optional.ofNullable(klass.cast(object));
    }

Lớp con:

class Child1 extend Parent implements Child1Specific {
    Child1() {
        registerCapability(Child1Specific.class, this);
    }
}

Hoặc năng động hơn:

class Child1 extends Parent {
    private Child1Specific specific = new Child1Specific() {
        ... Parent.this ...
    };
    Child1() {
        registerCapability(Child1Specific.class, specific);
    }
}

-3

Thêm một phương thức trừu tượng child1SpecificMethod () trong lớp Parent (bạn cần đánh dấu lớp là trừu tượng) và cung cấp triển khai của nó trong lớp con tương ứng.

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.