적절한 방법으로 조건부를 다형성으로 대체 하시겠습니까?


10

프로토콜 (Swift 프로그래밍 언어 측면에서 두 가지 클래스 Dog와 Java / C #의 인터페이스)을 Cat준수하는 클래스 를 모두 고려하십시오 Animal.

우리는 개와 고양이의 혼합 목록을 보여주는 화면이 있습니다. 거기 Interactor클래스는 뒤에서 그 핸들 로직은.

이제 고양이를 삭제하려고 할 때 사용자에게 확인 알림을 표시하려고합니다. 그러나 경고없이 개를 즉시 삭제해야합니다. 조건부가있는 방법은 다음과 같습니다.

func tryToDeleteModel(model: Animal) {
    if let model = model as? Cat {
        tellSceneToShowConfirmationAlert()
    } else if let model = model as? Dog {
        deleteModel(model: model)
    }
}

이 코드를 어떻게 리팩터링 할 수 있습니까? 그것은 분명히 냄새

답변:


9

프로토콜 유형 자체가 동작을 결정 하게합니다 . 구현 클래스 자체를 제외하고 프로그램 전체에서 모든 프로토콜을 동일하게 취급하려고 합니다. 이 방법을 사용하는 것은 Liskov의 대체 원칙을 존중하는 것입니다. Liskov의 대체 원칙은 하나 Cat또는 Dog(또는 Animal잠재적으로 아래에있을 수있는 다른 프로토콜 )을 전달하고 무관심하게 작동해야한다고 말합니다.

아마 당신 은 and에 의해 구현 될 isCriticalfunc을 추가 할 것 입니다. 구현 하면 false가 반환되고 구현 하면 true가 반환됩니다.AnimalDogCatDogCat

그 시점에, 당신은 단지 할 필요가있을 것입니다 (구문이 맞지 않으면 사과드립니다. Swift의 사용자는 아닙니다) :

func tryToDeleteModel(model: Animal) {
    if model.isCritical() {
        tellSceneToShowConfirmationAlert()
    } else {
        deleteModel(model: model)
    }
}

거기 만 작은 문제이며, 그것은 것입니다 DogCat결정하지 않는 자체 그들은 의미 프로토콜입니다 isCritical반환, 모든 구현 클래스에이를 떠나는 것은 스스로 결정할 수 있습니다. 구현이 많으면 확장 가능한 클래스를 만들 Cat거나 Dog이미 올바르게 구현 isCritical하고 재정의 할 필요가없는 모든 구현 클래스를 효과적으로 지우는 것이 isCritical좋습니다.

이것이 귀하의 질문에 대한 답변이 아닌 경우 의견을 작성하여 그에 따라 답변을 확장하겠습니다!


이 질문의 문에서 조금 불분명하지만, Dog하고 Cat있는 동안, 클래스로 설명 Animal하는 각 클래스에 의해 구현 것 프로토콜입니다. 따라서 질문과 답변 사이에 약간의 불일치가 있습니다.
Caleb

모델이 확인 팝업을 표시할지 여부를 결정하도록 제안 하시겠습니까? 그러나 고양이가 10 마리 만 표시되는 경우에만 팝업 표시와 같이 논리가 많은 경우 어떻게해야합니까? 논리는 Interactor현재 상태에 달려 있습니다
Andrey Gordeev

예, 불분명 한 질문에 대해 죄송합니다. 편집 한 내용이 거의 없습니다. 더 분명
해져야합니다

1
이런 종류의 동작은 모델에 연결되어서는 안됩니다. 그것은 실체 자체가 아니라 상황에 달려있다. 나는 고양이와 개가 POJO 일 가능성이 높다고 생각한다. 행동은 다른 곳에서 다루어야하며 상황에 따라 바뀔 수 있어야합니다. Cat 또는 Dog에서 동작에 의존하는 동작 또는 방법 위임은 이러한 클래스에서 너무 많은 책임을 유발합니다.
Grégory Elhaimer

@ GrégoryElhaimer 행동을 결정하지는 않습니다. 단지 중요한 클래스인지 아닌지를 말하는 것입니다. 중요한 클래스인지 알아야하는 프로그램 전체의 동작은 평가하고 이에 따라 행동 할 수 있습니다. 이 모두 얼마나 인스턴스를 구별 속성 참 인 경우 CatDog처리는, 그것과의 일반적인 특성해야 할 수 있습니다 Animal. 다른 작업을 수행하면 나중에 유지 관리 두통이 발생합니다.
Neil

4

말해 대 물어

당신이 보여주고있는 조건부 접근은 " ask "라고 부를 것 입니다. 소비 클라이언트가있는 곳이다 묻는다 "종류를 무엇?" 그에 따라 동작과 객체와의 상호 작용을 사용자 정의합니다.

이것은 우리가 " tell " 이라고 부르는 대안과 대조됩니다 . tell을 사용하면 더 많은 작업을 다형성 구현으로 푸시 할 수 있으므로 조건부없이 사용 가능한 클라이언트 코드가 더 단순하고 가능한 구현에 관계없이 일반적입니다.

확인 경고를 사용하기 위해 인터페이스의 명시적인 기능을 만들 수 있습니다. 따라서 선택적으로 사용자를 확인하고 확인 부울을 리턴하는 부울 메소드가있을 수 있습니다. 확인하고 싶지 않은 클래스에서는 단순히로 재정의합니다 return true;. 다른 구현에서는 확인을 사용할지 여부를 동적으로 결정할 수 있습니다.

소비하는 클라이언트는 작업중인 특정 서브 클래스에 관계없이 항상 확인 메소드를 사용하므로 상호 작용 이 ask 대신에 알려줍니다 .

(또 다른 접근 방식은 확인을 삭제로 푸시하는 것이지만 삭제 작업이 성공할 것으로 예상되는 고객을 놀라게 할 것입니다.)


모델이 확인 팝업을 표시할지 여부를 결정하도록 제안 하시겠습니까? 그러나 고양이가 10 마리 만 표시되는 경우에만 팝업 표시와 같이 논리가 많은 경우 어떻게해야합니까? 논리는 Interactor지금 상태에 달려 있다
Andrey Gordeev

2
예, 다른 질문이므로 다른 답변이 필요합니다.
Erik Eidt

2

확인이 필요한지 여부를 결정하는 것은 Cat클래스 의 책임 이므로 해당 작업을 수행 할 수 있도록합니다. Kotlin을 모르므로 C #으로 내용을 표현하겠습니다. 바라건대 아이디어도 Kotlin으로 이전 될 수 있기를 바랍니다.

interface Animal
{
    bool IsOkToDelete();
}

class Cat : Animal
{
    private readonly Func<bool> _confirmation;

    public Cat (Func<bool> confirmation) => _confirmation = confirmation;

    public bool IsOkToDelete() => _confirmation();
}

class Dog : Animal
{
    public bool IsOkToDelete() => true;
}

그런 다음 Cat인스턴스를 만들 때을 제공하면 삭제하려면 OK이면 TellSceneToShowConfirmationAlert반환해야합니다 true.

var model = new Cat(TellSceneToShowConfirmationAlert);

그리고 당신의 기능은 다음과 같습니다.

void TryToDeleteModel(Animal model) 
{
    if (model.IsOKToDelete())
    {
        DeleteModel(model)
    }
}

1
삭제 논리가 모델로 이동되지 않습니까? 이것을 처리하기 위해 다른 객체를 사용하는 것이 낫지 않습니까? ApplicationService 내부의 Dictionary <Cat>와 같은 데이터 구조 일 수 있습니다. 고양이가 있는지 확인하고 확인 알림이 발생하는지 확인하십시오.
keelerjr12

@ keelerjr12, 삭제에 대한 확인이 Cat클래스에 필요한지를 결정하는 책임을 옮깁니다 . 나는 그것이 그것이 속한 곳이라고 주장하고 싶습니다. 확인이 이루어지는 방법 (주입)을 결정하지 못하고 자체를 삭제하지 않습니다. 따라서 삭제 논리는 모델로 이동하지 않습니다.
David Arno

2
이 접근법은 클래스 자체에 수많은 UI 관련 코드로 이어질 것 같습니다. 클래스를 여러 UI 계층에서 사용하려는 경우 문제가 커집니다. 그러나 이것이 비즈니스 엔티티가 아닌 ViewModel 유형 클래스 인 경우 적절 해 보입니다.
Graham

@Graham, 그렇습니다.이 접근법의 위험은 분명합니다 . TellSceneToShowConfirmationAlert의 인스턴스에 쉽게 주입 할 수 있다는 것 입니다 Cat. 쉽지 않은 상황 (예 :이 기능이 심층적 인 다중 계층 시스템)에서는이 방법이 좋지 않습니다.
David Arno

1
내가 뭘했는지 정확히 비즈니스 엔터티와 ViewModel 클래스 비즈니스 영역에서 Cat은 UI 관련 코드를 알아야합니다. 우리 가족 고양이는 아무에게도 알리지 않습니다. 감사!
keelerjr12 년

1

방문자 패턴을 찾아 보라고 조언합니다. Java로 작은 구현을했습니다. 나는 Swift에 익숙하지 않지만 쉽게 조정할 수 있습니다.

방문객

public interface AnimalVisitor<R>{
    R visitCat();
    R visitDog();
}

당신의 모델

abstract class Animal { // can also be an interface like VisitableAnimal
    abstract <R> R accept(AnimalVisitor<R> visitor);
}

class Cat extends Animal {
    public <R> R accept(AnimalVisitor<R> visitor) {
         return visitor.visitCat();
     }
}

class Dog extends Animal {
    public <R> R accept(AnimalVisitor<R> visitor) {
         return visitor.visitDog();
     }
}

방문자에게 전화

public void tryToDelete(Animal animal) {
    animal.accept( new AnimalVisitor<Void>() {
        public Void visitCat() {
            tellSceneToShowConfirmation();
            return null;
        }

        public Void visitDog() {
            deleteModel(animal);
            return null;
        }
    });
}

원하는만큼 AnimalVisitor를 구현할 수 있습니다.

예:

public void isColorValid(Color color) {
    animal.accept( new AnimalVisitor<Boolean>() {
        public Boolean visitCat() {
            return Color.BLUE.equals(color);
        }

        public Boolean visitDog() {
            return true;
        }
    });
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.