Смешанная область действия, ориентированная на протокол Swift

У меня есть концептуальный вопрос по протокольно-ориентированному программированию. Допустим, я создаю протокол Foo и хочу расширить Foo с помощью функции action() в расширении протокола. action() всегда будет в основном одним и тем же, независимо от того, кто его реализует, поэтому я не хочу повторять этот код. Единственная часть action(), которая изменяется, — это prop, а prop должен быть свойством экземпляра. Таким образом, моя соответствующая структура должна просто иметь определенную поддержку, и тогда действие будет работать правильно. Этот код в порядке и выглядит так:

protocol Foo {
    var prop : String {get set}
}
extension Foo {
    func action() -> String {
        return prop + ": Applied Action."
    }
}

Но вот загвоздка. Допустим, реквизит — это информация, к которой я не хочу, чтобы другие классы имели доступ. Я хочу, чтобы они обращались к нему только через action(). В текущем swift свойство должно быть как минимум внутренним, потому что протокол является внутренним, но мне нужно, чтобы оно было закрытым, чтобы потребляющие классы не могли случайно изменить свойство или прочитать информацию, к которой я хочу, чтобы они имели доступ только через action() . Я мог бы добавить action() в протокол, но тогда мне нужно переписывать action() каждый раз, когда структура соответствует требованиям.

Этот же пример можно расширить и до функции:

protocol Foo {
     //customization point by conforming class
    func customAction(str: String) -> String
}
extension Foo {
    func action() -> String {
        //Do some default work
        let str = "some string created from previous default work"
        return customAction(str: str)
    }
}

action() всегда будет одной и той же реализацией, за исключением cusotmAction(). Итак, мне нужны соответствующие классы для реализации customAction(), но я не хочу, чтобы какие-либо внешние классы вызывали customAction(), поскольку он обеспечивает только определенное поведение, которое следует использовать только внутри action(). Поэтому мне нужно, чтобы customAction() был закрытым, но опять же, это невозможно.

Итак, вот мой вопрос, это просто сбой в протокольно-ориентированном программировании? Как я могу использовать протоколы для написания кода такого типа, не предоставляя слишком много возможностей prop/customAction() или не переписывая один и тот же код внутри action() снова и снова? Этот пример довольно прост, но я сталкиваюсь с более сложными версиями той же проблемы. Есть ли другой способ взглянуть на эту проблему с точки зрения POP, или мне следует искать более объектно-ориентированный подход?


person Luke Street    schedule 21.07.2017    source источник


Ответы (1)


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

struct Foo {
    init(prop: String) {
        self.prop = prop
    }

    func action() -> String {
        return prop + ": Applied Action."
    }

    private let prop: String
}


class Test {
    let foo = Foo(prop: "TEST")
}

В этом случае Test может определить свой собственный prop почти так же (но не совсем так), как если бы он использовал протокол. При этом теперь вы можете вызывать все функции foo внутри экземпляра Test, не имея доступа к базовым переменным. Делая это таким образом (с композицией), вы также избавляетесь от проблем наследования, которые в первую очередь заставляют людей использовать POP. Надеюсь, это поможет!

person Adam H.    schedule 21.07.2017
comment
Это кажется хорошим решением для первой ситуации, когда нужно настроить только переменную, но как насчет второй ситуации, когда это функция? Единственный способ, которым я могу думать, чтобы сделать эту работу, - это передать закрытие в init? Спасибо! - person Luke Street; 21.07.2017
comment
В Swift вы можете передать функцию как переменную и сохранить ее так же, как и любой другой объект. - person Adam H.; 21.07.2017
comment
Это кажется хорошим возможным решением. Тем не менее, мне интересно узнать, есть ли еще какие-то решения этой проблемы, основанные на POP. - person Luke Street; 21.07.2017