Доступ к объектным методам с помощью композиции?


Это, вероятно, очень простая вещь, но я просто не могу разобраться в этом самостоятельно. При использовании композиции, каков лучший способ получить доступ к методам " внутреннего объекта?"Каждый способ, который я могу разработать, кажется, нарушает тот или иной принцип ОО.

Это своего рода очень широкая тема, но я приведу пример, чтобы сделать вещи ясными, насколько это возможно. Код написан на java, но я считаю, что этот вопрос применим практически к любому языку, использующему ООП.
Class Shelf {
    private Book book;
}

Class Book {
    public void turnPage() {
        //do stuff
    }
}

Что будет ли лучшим способом получить доступ к методам книги, когда у вас есть только доступ к объекту полки? Несколько мест, которые я читал, предлагают методы обертывания внутри полки, так как это следует закону Деметры. Однако это не кажется лучшим вариантом для каждого случая.

Во-первых, многие методы внутреннего объекта могут не иметь ничего общего с главным классом, поэтому нет никакого смысла иметь главный класс, реализующий оболочки для каждого метода, и это, скорее всего, сломает единственный метод. Принцип Ответственности. Кроме того, если бы внутренний объект использовался для композиции в большом количестве других классов, все они должны были бы делать то же самое, это привело бы к большому количеству ненужного дублирования кода, делая ваш код гораздо менее сухим.

Мой первый инстинкт состоял в том, чтобы иметь простой метод getBook (), чтобы дать доступ к книге для непосредственного использования методов. Более разумно взять книгу с полки и перевернуть страницу, а не приказывать полке перевернуть страницу своей книги для вас. Однако это, по-видимому, нарушает как инкапсуляцию, так и закон Деметры. То же самое относится и к тому, чтобы сделать книгу "переменная" общедоступной.

Я что-то упустил? Или все усложняет? Я, кажется, не могу поверить в это, так что я был бы очень признателен за помощь.

1 3

1 ответ:

В этом случае, скорее всего, у вас будут книги, а не одна книга на полке. Для меня имеет смысл вернуть конкретную книгу по идентификатору, который является уникальным для полки, и оперировать ею.

В этом случае я бы не стал возвращать коллекцию книг, которая затем нарушит инкапсуляцию и закон Деметры.

Так, например:

public class Shelf {
    private Map<Long, Book> books = new HashMap();

    // Class Code

    public Book getBook(Long id) {
       return books.get(id);
    }
}

Что-то вроде этого не нарушает инкапсуляцию, потому что вы не раскрываете внутренние компоненты класса. Если ты когда-нибудь захочешь чтобы использовать список вместо карты, изменения не должны затрагивать ничего за пределами класса.

И в этом случае имеет смысл вернуть книгу, а не создавать метод turnPage() в классе Shelf. Это то же самое, как если бы у вас было свойство name в Book, которое имеет тип string. Вы же не создадите метод nameSubstring() в Book только для того, чтобы оперировать name, верно? Так зачем же создавать turnPage() в shelf только для того, чтобы оперировать Book?