Sorma ve Sorgu Ayırma Komutu arasında nasıl seçim yapabilirim?


25

Söyle sorma prensibi diyor ki:

Nesnelere ne yapmalarını istediğinizi söylemeye çalışmalısınız; Onlara durumları hakkında soru sorma, bir karar verme ve sonra onlara ne yapacaklarını söyleme.

Sorun şu ki, arayan kişi olarak, daha sonra nesnenin durumunu değiştiren sonuçlanan nesnenin durumuna dayalı kararlar almamanız gerekir. Uyguladığınız mantık muhtemelen sizinki değil nesnenin sorumluluğundadır. Nesnenin dışında kararlar almanız onun kapsüllemesini ihlal ediyor.

Basit bir örnek "Sor yok, söyle" olduğunu

Widget w = ...;
if (w.getParent() != null) {
  Panel parent = w.getParent();
  parent.remove(w);
}

ve söyle sürümü ...

Widget w = ...;
w.removeFromParent();

Peki ya removeFromParent yönteminin sonucunu bilmem gerekiyorsa? İlk tepkim, ebeveyn çıkarıldı mı yoksa çıkarılmadıysa, boolean ifadesini döndürecek olan RemoveParent öğesini değiştirmekti.

Ama daha sonra bunu yapmamasını söyleyen Komuta Sorgu Ayırma Paternine rastladım .

Her yöntemin bir eylemi gerçekleştiren bir komut veya arayana veri döndüren bir sorgu olması gerektiğini, ancak her ikisini de içermemesi gerektiğini belirtir. Başka bir deyişle, bir soru sormak cevabı değiştirmemelidir. Daha resmi olarak, metotlar yalnızca referans olarak saydam oldukları ve bu nedenle hiçbir yan etkisinin olmadığı durumlarda bir değer vermelidir.

Bu ikisi gerçekten birbirleriyle çelişiyor mu ve ikisi arasında nasıl seçim yapabilirim? Bununla ilgili Pragmatik Programcı veya Bertrand Meyer ile birlikte mi gideceğim?


1
Boole ile ne yapardın?
David

1
Kullanılabilirliği dengelemeden kodlama kalıplarına çok uzaklara
dalıyormuşsunuz

Yeniden boolean ... bu, geri çekilmesi kolay olan ancak aşağıdaki yazım işlemine benzer bir örnek, amaç operasyonun bir durumudur.
Dakotah Kuzey

2
Demek istediğim şu ki, "bir şeyleri geri getirmeye" odaklanmamanız, onunla ne yapmak istediğinize daha fazla odaklanmanız gerekiyor. Başka bir yorumda dediğim gibi, başarısızlığa odaklanırsanız, istisna kullanın, işlem tamamlandığında bir şey yapmak istiyorsanız, daha sonra geri arama veya olayları kullanın, bunun ne olduğunu günlüğe kaydetmek istiyorsanız Widget sorumluluğu ... neden ihtiyaçlarınızı soruyorum. Bir şeyi iade etmeniz gereken somut bir örnek bulamazsanız, belki bu ikisi arasında seçim yapmak zorunda kalmayacağınız anlamına gelir.
David

1
Komut Sorgu Ayırma bir model değil, bir prensiptir. Desenler, bir sorunu çözmek için kullanabileceğiniz bir şeydir. Prensipler, sorun yaratmamak için takip ettiğiniz şeydir.
Instagram Hesabındaki Resim ve Videoları candied_orange

Yanıtlar:


25

Aslında, örnek probleminiz zaten ayrışma eksikliğini göstermektedir.

Sadece biraz değiştirelim:

Book b = ...;
if (b.getShelf() != null) 
    b.getShelf().remove(b);

Bu gerçekten farklı değil, ancak daha belirgin hale getirir: Kitap neden raftan haberdar olsun ki? Basitçe söylemek gerekirse, olmamalı. Kitapların raflara bağımlılığını (anlam ifade etmeyen) tanıtır ve dairesel referanslar oluşturur. Hepsi kötü.

Aynı şekilde, bir widget'in ebeveyini bilmesi gerekli değildir. Söyleyeceksiniz: "Tamam, ancak pencere aracının kendisini doğru bir şekilde düzenleyebilmesi için veli ihtiyacı var." ve kaputun altında widget onun üst bilir ve ölçümlerini vb onlara göre dayalı kendi ölçümleri gerçekleştirmesine bunu sorar sormayın, söyle bu yanlış. Ebeveyn bütün çocuklarına, tüm gerekli bilgileri argüman olarak ileterek, yapmalarını söylemelidir. Bu yolla iki ebeveynde aynı widget kolayca bulunabilir (bunun gerçekten anlamlı olup olmadığı).

Kitap örneğine geri dönmek için - kütüphaneciye girin:

Librarian l = ...;
Book b = ...;
l.findShelf(b).remove(b);

Lütfen anlayın, anlamında artık sormadığımızı söyleme , sorma .

İlk versiyonda, raf kitabın bir özelliği idi ve siz istediniz. İkinci versiyonda kitap hakkında böyle bir varsayımda bulunmayız. Tek bildiğimiz kütüphanecinin bize kitabı içeren bir raf söyleyebileceği. Muhtemelen bunu yapmak için bir tür arama masasına güveniyor, ancak kesin olarak bilmiyoruz (her rafta tüm kitapların arasına da girebiliyordu) ve aslında bilmek istemiyoruz .
Kütüphanecilerin tepkisinin kendi durumuna veya güvenebileceği diğer nesnelerin cevabına bağlı olup olmadığına güvenmiyoruz. Kütüphaneciye rafı bulmasını söylüyoruz.
İlk senaryoda, bir kitap ile bir raf arasındaki ilişkiyi doğrudan kitap durumunun bir parçası olarak kodladık ve bu durumu doğrudan aldık. Bu çok kırılgan, çünkü kitabın geri verdiği rafın kitabın içerdiği varsayımını da yapıyoruz (bu, sağlamamız gereken bir kısıtlamadır, aksi takdirde removekitabın içinde bulunduğu raftan yapamayabiliriz ).

Kütüphanecinin tanıtımıyla, bu ilişkiyi ayrı ayrı modelliyoruz, bu nedenle endişelerin ayrılmasını sağlıyoruz.


Bunun çok geçerli bir nokta olduğunu düşünüyorum, ancak soruyu yanıtlamıyor. Sürümünüzde soru şudur: Shelf sınıfındaki remove (Book b) yönteminin bir geri dönüş değeri olmalı mı?
scarfridge

1
@scarfridge: Alt satırda, söyleme, sorma endişelerin uygun şekilde ayrılmasının bir sonucudur. Eğer düşünürseniz, bu yüzden soruyu cevaplarım. En azından öyle düşünürdüm;)
back2dos

1
@scarfridge Bir dönüş değeri yerine, başarısızlık durumunda çağrılan bir işlev / temsilci geçebilir. Eğer başarılı olursa, bitirdiniz değil mi?
Yahu’yu

2
@ Blessinah Bu bana aşırı tasarlanmış görünüyor. Şahsen ben Komut-sorgu ayrılmasının gerçek (çok iş parçacıklı) gerçek dünyada daha ideal olduğunu düşünüyorum. IMHO, yan etkileri olan bir yöntemin, yöntemin adı açıkça belirtildiği sürece, nesnenin durumunu değiştireceği sürece bir değer döndürmesi tamamdır. Bir yığının pop () yöntemini göz önünde bulundurun. Ancak, sorgu adına sahip bir yöntemin yan etkileri olmamalıdır.
scarfridge

13

Sonucu bilmek gerekirse, o zaman; bu senin ihtiyacın.

"Yöntemler yalnızca referans niteliğinde saydamlarsa ve dolayısıyla hiçbir yan etkiye sahip değilse, bir değer döndürmelidir" ifadesi (özellikle programınızı eşzamanlılık veya diğer nedenlerle, işlevsel bir tarzda yazıyorsanız), iyi bir kılavuzdur , ancak mutlak değil.

Örneğin, bir dosya yazma işleminin sonucunu bilmeniz gerekebilir (doğru veya yanlış). Bu , değeri döndüren, ancak her zaman yan etkiler üreten bir yöntem örneğidir ; Bunun yolu yok.

Komut / Sorgu Ayırma işlemini yerine getirmek için, dosya işlemini bir yöntemle gerçekleştirmeniz ve ardından sonucu başka bir yöntemle kontrol etmeniz gerekir , çünkü sonucu sonucu oluşturan yöntemden ayırır. Dosya çağrısı ve durum kontrolü arasında nesnenizin durumuna bir şey olursa ne olur?

Sonuç olarak şudur: Programın herhangi bir yerinde karar vermek için bir yöntem çağrısının sonucunu kullanıyorsanız, Sorma deme komutunu ihlal etmiyorsunuzdur. Öte yandan, o nesneye yapılan bir yöntem çağrısına dayanarak bir nesne için kararlar alıyorsanız, kapsüllemeyi korumak için bu kararları nesnenin kendisine taşımalısınız .

Komut Sorgu Ayrımı ile hiçbir şekilde çelişkili değil; Aslında, onu daha da zorlar, çünkü artık statü amaçları için harici bir yöntem ortaya koyma gereği yoktur.


1
Bir örnek olarak, "yazma" işlemi başarısız olursa, bir istisna atılması gerektiği, bu nedenle "statü alma" yöntemine gerek olmadığı söylenebilir.
David

@David: Öyleyse OP'nin örneğine geri dönelim, bu bir değişiklik gerçekleştiğinde doğru, geri dönmediyse yanlış olur. Orada bir istisna yapmak isteyeceğinden şüpheliyim.
Robert Harvey,

OP'nin örneği bile benim için doğru gelmiyor. Çıkarmanın mümkün olmadığı durumlarda fark edilmek istersiniz, bu durumda istisna kullanırsınız veya gerçekte bir widget kaldırıldığında fark edilmek istersiniz, bu durumda olayı veya geri çağırma mekanizmasını kullanabilirsiniz. "Komut Sorgu Ayırma Örüntüsüne" saygı duymak istemeyeceğiniz gerçek bir örnek görmüyorum
David

@David: Açıklığa kavuşturmak için cevabımı düzenledim.
Robert Harvey,

Bu konuda çok iyi bir noktaya değinmemekle birlikte, komut sorgusu ayrılmasının arkasındaki bütün fikir, dosyayı yazmak için komutu yayınlayanın sonucu umursamadığıdır - en azından belirli bir anlamda (bir kullanıcı daha sonra olabilir) tüm son dosya işlemlerinin bir listesini çıkarın, ancak bu ilk komutla ilişkilendirilmez). Bir komut tamamlandıktan sonra eylemde bulunmanız gerekiyorsa, sonucu önemseseniz de dikkate almasanız da, bunu yapmanın doğru yolu , asıl komut ve / veya bununla ilgili ayrıntıları içeren olayları kullanan asenkron bir programlama modelindedir . sonuç.
Aaron,

2

İlk içgüdünle gitmen gerektiğini düşünüyorum. Bazen tasarım, nesneyle iletişim kurarken derhal karmaşıklığı ortaya koymak için zorla kullanılmalıdır, böylece zorla işlendikten hemen sonra düzgünce başa çıkabilmek yerine, nesneyi kendi üzerinde tutmaya çalışmak ve tüm kanlı ayrıntıları gizlemek ve temiz bir şekilde zorlaştırmak gerekir. temel varsayımları değiştirin.

Eğer bir nesne size eşdeğerleri openve closedavranışları uygularsa teklif eder ve basit bir atomik görev olacağını düşündüğünüz bir şey için bir boolean getiri değeri gördüğünüzde derhal ne anladığınızı anlamaya başlarsınız.

Daha sonra nasıl başa çıkacağınız şey sizin. Üzerinde bir widget türü olan bir soyutlama yaratabilirsiniz removeFromParent(), ancak her zaman düşük seviyeli bir geri dönüşe sahip olmalısınız, aksi halde erken varsayımlarda bulunuyordunuz.

Her şeyi basitleştirmeye çalışmayın. Zarif ve çok masum görünen bir şeye güveniyorsanız, yalnızca en kötü anlarda bunun gerçek dehşetini gerçekleştirmek için hayal kırıklığı yaratan hiçbir şey yok.


2

Tanımladığınız şey Komut Sorgu Ayırma ilkesine bilinen bir "istisna" dır.

Bu makalede Martin Fowler böyle bir istisnayı nasıl tehdit edeceğini açıklar.

Meyer, kesinlikle komut sorgusu ayırmayı kullanmaktan hoşlanıyor, ancak istisnalar da var. Bir yığın açmak, durumu değiştiren iyi bir sorgu örneğidir. Meyer, bu yönteme sahip olmaktan kaçınabileceğinizi doğru söylüyor, ancak yararlı bir deyimdir. Bu yüzden yapabildiğim zaman bu prensibi izlemeyi tercih ederim, ancak babamı almak için onu kırmaya hazırım.

Örneğinizde aynı istisnayı göz önünde bulundururum.


0

Komut-Sorgu ayrıştırmasının yanlış anlaşılması şaşırtıcı derecede kolaydır.

Size sistemimde söylersem, aynı zamanda bir sorgudan değer de veren bir komut var ve “Ha! İhlal ediyorsunuz!” Diyorsunuz. silahı atlıyorsun.

Hayır, yasak olan bu değil.

Yasak olan, bu komutun bu sorguyu yapmanın SADECE yol olduğu zamandır. Hayır. Soru sormak için devlet değiştirmek zorunda olmamalıyım. Bu her durum değiştirdiğimde gözlerimi kapatmam gerektiği anlamına gelmiyor.

Yapmamın bir önemi yok, çünkü yine de tekrar açacağım. Bu aşırı reaksiyonun tek yararı tasarımcıların tembelleşmemelerini sağlamak ve durum değiştirmeyen sorguyu eklemeyi unutmaktı.

Asıl anlam o kadar belirsiz ki, tembel insanların ayarcıların boş vermeleri gerektiğini söylemeleri daha kolay. Bu, gerçek kuralı ihlal etmediğinizden emin olmanızı kolaylaştırır, ancak gerçek bir acı haline gelebilecek aşırı bir tepkidir.

Akıcı arayüzler ve iDSL'ler bu aşırı tepkiyi her zaman "ihlal ediyor". Eğer aşırı tepki verirsen, görmezden gelinen çok fazla güç var.

Bir komutun sadece bir şey yapması gerektiğini ve bir sorgunun sadece bir şey yapması gerektiğini savunabilirsiniz. Ve birçok durumda bu iyi bir nokta. Bir komutu izlemesi gereken tek bir sorgu olduğunu kim söyleyebilir? İyi ama bu komut sorgusu ayrımı değil. Tek sorumluluk ilkesi bu.

Bu şekilde baktım ve bir yığın pop bazı tuhaf bir istisna değildir. Bu tek bir sorumluluktur. Pop, yalnızca göz atmayı unuttuğunuzda komut sorgusu ayrımını ihlal eder.

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.