Koşullu'yu polimorfizm ile uygun bir şekilde değiştir?


10

İki sınıfı düşünün Dogve Cather ikisi de Animalprotokole uygun (Swift programlama dili açısından. Java / C # arayüzünde olurdu).

Köpeklerin ve kedilerin karışık bir listesini gösteren bir ekranımız var. Orada Interactorsınıf perde arkasında o kolları mantık.

Şimdi bir kediyi silmek istediğinde kullanıcıya bir onay uyarısı sunmak istiyoruz. Bununla birlikte, köpeklerin herhangi bir uyarı olmadan derhal silinmesi gerekir. Şartlı yöntem şöyle görünür:

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

Bu kod nasıl yeniden düzenlenebilir? Açıkçası kokuyor

Yanıtlar:


9

Protokol türünün kendisinin davranışı belirlemesine izin veriyorsunuz . Uygulayıcı sınıfın kendisi dışında tüm protokolleri programınız boyunca aynı şekilde işlemek istiyorsunuz . Eğer geçmek gerekir diyor Liskov en değiştirme prensibi, saygı bu şekilde olduğu takdirde ya Catya Dog(veya daha sonra altında gelebilecek tüm diğer protokoller Animalpotansiyel) ve kayıtsızca çalışmak zorunda.

Muhtemelen hem ve tarafından uygulanacak bir isCriticalişlev eklersiniz . Uygulayan her şey yanlış döndürür ve uygulayan her şey doğru olur.AnimalDogCatDogCat

Bu noktada, sadece yapmanız gerekir (Sözdizimi doğru değilse özür dilerim. Swift'in bir kullanıcısı değil):

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

Bununla ilgili sadece küçük bir sorun var ve bu da Dogve Catprotokoller, yani kendi başlarına ne isCriticaldöndürdüğünü belirlemiyorlar , bunu kendileri için karar vermek için her uygulayıcı sınıfa bırakıyorlar. Çok fazla uygulamanız varsa, büyük olasılıkla doğru şekilde uygulayan Catveya tüm uygulayan sınıfları geçersiz kılma ihtiyacından etkili bir şekilde temizleyen genişletilebilir bir sınıf oluşturmak için zaman ayırmaya değer olacaktır .DogisCriticalisCritical

Bu, sorunuza cevap vermezse, lütfen yorumları yazın ve cevabımı buna göre genişleteceğim!


Sorunun açıklamasında biraz belirsizdir Dogve Catsınıflar olarak tanımlanırken Animal, bu sınıfların her biri tarafından uygulanan bir protokoldür. Yani soru ve cevabınız arasında biraz uyuşmazlık var.
Caleb

Yani modelin bir onay pop-up'ı gösterip göstermeyeceğine karar vermesini mi öneriyorsunuz? Peki, eğer sadece 10 kedi gösteriliyorsa gösteri pop-up'ı gibi ağır bir mantık varsa ne olur? Mantık Interactorşimdi duruma bağlı
Andrey Gordeev

Evet, belirsiz soru için üzgünüm, birkaç düzenleme yaptım. Şimdi daha açık olmalı
Andrey Gordeev

1
Bu tür davranışlar modele bağlı olmamalıdır. İşletmenin kendisine değil bağlama bağlıdır. Kedi ve Köpeklerin POJO olma olasılığı daha yüksek. Davranışlar başka yerlerde ele alınmalı ve bağlama göre değişebilmelidir. Kedi veya Köpek'te davranışların veya davranışların dayandırılacağı yöntemlerin devredilmesi, bu tür sınıflarda çok fazla sorumluluğa yol açacaktır.
Grégory Elhaimer

@ GrégoryElhaimer Lütfen davranışın belirleyici olmadığını unutmayın. Sadece eleştirel bir sınıf olup olmadığını belirtiyor. Program boyunca eleştirel bir sınıf olup olmadığını bilmesi gereken davranışlar daha sonra buna göre değerlendirilebilir ve hareket edebilir. Bu hem nasıl örneklerini ayıran bir özellik gerçekten ise Catve Dogişlenir, bu ve ortak bir özellik olmalıdır olabilir Animal. Başka bir şey yapmak daha sonra bakım baş ağrısı istiyor.
Neil

4

Söyle ve Sor

Gösterdiğiniz koşullu yaklaşıma " sor " deriz . Tüketici müşteri "ne tür birisiniz ?" ve nesnelerle davranışlarını ve etkileşimlerini buna göre özelleştirir.

Bu, " tell " dediğimiz alternatifin aksine . Kullanılması bahsedildiğini , sen tüketen istemci kod Koşullamalar olmadan, basit ve yaygın olursa olsun mümkün uygulamaların böylece, polimorfik uygulamalara içine işin daha itin.

Onaylama uyarısı kullanmak istediğiniz için, bunu arayüzün açık bir kabiliyeti haline getirebilirsiniz. Bu nedenle, isteğe bağlı olarak kullanıcıyı denetleyen ve onaylama booleanını döndüren bir boolean yönteminiz olabilir. Onaylamak istemeyen sınıflarda sadece ile geçersiz kılınırlar return true;. Diğer uygulamalar, onayı kullanmak isteyip istemediklerini dinamik olarak belirleyebilir.

Tüketici müşteri, çalıştığı belirli alt sınıfa bakılmaksızın her zaman onay yöntemini kullanır ve bu da etkileşimi sormak yerine anlatır .

(Başka bir yaklaşım, onayı silinmeye itmek olacaktır, ancak bu, bir silme işleminin başarılı olmasını bekleyen müşterileri şaşırtmaktadır.)


Yani modelin bir onay pop-up'ı gösterip göstermeyeceğine karar vermesini mi öneriyorsunuz? Peki, eğer sadece 10 kedi gösteriliyorsa gösteri pop-up'ı gibi ağır bir mantık varsa ne olur? Mantık Interactorşimdi duruma bağlı
Andrey Gordeev

2
Tamam, evet, bu farklı bir soru, farklı bir cevap gerektiriyor.
Erik Eidt

2

Bir teyit gerekip gerekmediğini belirlemek Catsınıfın sorumluluğundadır , bu yüzden bu işlemi yapmasını sağlayın . Kotlin'i tanımıyorum, bu yüzden C # 'da bir şeyler ifade edeceğim. Umarım fikirler daha sonra Kotlin'e de aktarılabilir.

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;
}

Daha sonra, bir Catörnek oluştururken , bunu silmek için Tamam'ı TellSceneToShowConfirmationAlertdöndürmeniz gerekir true:

var model = new Cat(TellSceneToShowConfirmationAlert);

Ve sonra işleviniz olur:

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

1
Bu silme mantığını modele taşımaz mı? Bunu yapmak için başka bir nesne kullanmak daha iyi olmaz mıydı? Muhtemelen bir ApplicationService içindeki Dictionary <Cat> gibi bir veri yapısı; Kedinin var olup olmadığını ve onay alarmını tetikleyip tetiklemediğini kontrol edin.
keelerjr12

@ keelerjr12, silme için bir onay gerekip gerekmediğini belirleme sorumluluğunu sınıfa taşır Cat. Onun ait olduğu yer olduğunu iddia ediyorum. Bu onaylamanın nasıl elde edildiğine (enjekte edildi) karar vermez ve kendini silmez. Yani hayır, silme mantığını modele taşımaz.
David Arno

2
Bu yaklaşımın, sınıfın kendisine eklenen tonlarca ve kullanıcı arayüzü ile ilgili kodlara yol açacağını hissediyorum. Sınıfın birden çok kullanıcı arayüzü katmanında kullanılması amaçlanıyorsa, sorun büyür. Ancak, bu bir işletme varlığı yerine ViewModel türü bir sınıfsa, uygun görünür.
Graham

@Graham, evet bu kesinlikle bu yaklaşımda bir risk: TellSceneToShowConfirmationAlertbir örneğe enjekte etmenin kolay olmasına dayanıyor Cat. Bunun kolay bir şey olmadığı durumlarda (bu işlevin derin bir düzeyde yer aldığı çok katmanlı bir sistemde olduğu gibi), bu yaklaşım iyi olmaz.
David Arno

1
Tam olarak ne alıyordum. Bir işletme varlığı ile ViewModel sınıfının karşılaştırması. İşletme alanında, bir kedi kullanıcı arayüzü ile ilgili kodu bilmemelidir. Aile kedim kimseyi uyarmaz. Teşekkürler!
keelerjr12

1

Ziyaretçi kalıbına gitmeyi tavsiye ederim. Java ile küçük bir uygulama yaptım. Swift'e aşina değilim, ama kolayca adapte edebilirsiniz.

Ziyaretçi

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

Modeliniz

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();
     }
}

Ziyaretçiyi aramak

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

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

İstediğiniz kadar AnimalVisitor uygulamasına sahip olabilirsiniz.

Misal:

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

        public Boolean visitDog() {
            return true;
        }
    });
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.