En az şaşkınlık (POLA) ve arayüzler ilkesi


17

C ++ öğreniyorken yüzyılın iyi bir çeyreği, arayüzlerin affedici olması ve mümkün olduğunca tüketicilerin kaynak veya belgelere erişemeyeceği için yöntemlerin çağrıldığı sırayı umursamayacağı öğretildi. bu.

Ancak, ne zaman genç programcılara ve kıdemli geliştiricilere beni kulak misafiri olduysa, bunun gerçekten bir şey olup olmadığını veya modası geçmiş olup olmadığını merak ettim.

Çamur kadar açık mı?

Bu yöntemlerle bir arayüz düşünün (veri dosyaları oluşturmak için):

OpenFile
SetHeaderString
WriteDataLine
SetTrailerString
CloseFile

Şimdi elbette bunları sırayla gidebilirsin, ama dosya adını (düşün a.out) ya da hangi başlık ve römork dizesini dahil ettiğini umursamadığını söyleyebilirsin , sadece arayabilirsin AddDataLine.

Daha az uç bir örnek, başlıkları ve römorkları atlamak olabilir.

Başka bir dosya açılmadan önce başlık ve römork dizeleri ayarlamak olabilir.

Bu, tanınan bir arayüz tasarımı ilkesi mi yoksa ad verilmeden önce sadece POLA yolu mu?

NB, bu arayüzün minutia'larında bataklığa düşmüyor, bu soru için sadece bir örnek.


10
"En az şaşkınlık" ilkesi, Kullanıcı arabirimi tasarımında "Uygulama programcısı arabirimi" tasarımından çok daha yaygındır . Bunun nedeni, bir web sitesi veya program kullanıcısının, onu kullanmadan önce hiçbir talimat okuması beklenemezken, bir programcının en azından prensip olarak, onlarla programlamadan önce API belgelerini okuması beklenir.
Kilian Foth


7
@KilianFoth: Wikipedia'nın bu konuda yanlış olduğundan eminim - POLA sadece kullanıcı arayüzü tasarımı ile ilgili değil, aynı zamanda işlev ve sınıf tasarımı için Bob Martin tarafından da "en az sürpriz ilkesi" terimi kullanılıyor. "Temiz Kod" kitabı.
Doc Brown

2
Genellikle, değişmez bir arayüz daha iyidir. Yapım aşamasında ayarlamak istediğiniz tüm verileri belirtebilirsiniz. Belirsizlik kalmadı ve sınıfın yazılması daha kolay hale geldi. (Bazen bu şema elbette mümkün değildir.)
usr

4
POLA'nın API'lara uygulanmadığı konusunda tamamen aynı fikirde değilim. Bir insanın diğer insanlar için yarattığı her şey için geçerlidir. İşler beklendiği gibi hareket ettiğinde, kavramsallaştırılması daha kolaydır ve bu nedenle insanların daha az çaba ile daha fazla şey yapmasına izin veren daha düşük bir bilişsel yük oluştururlar.
Robot Gort

Yanıtlar:


25

En az şaşkınlık ilkesine bağlı kalmanın bir yolu , ISS ve SRP veya hatta KURU gibi diğer ilkeleri dikkate almaktır .

Verdiğiniz belirli örnekte, öneri dosyayı manipüle etmek için belirli bir bağımlılık bağımlılığı gibi görünüyor; ancak API'niz hem dosya erişimini hem de SRP'nin ihlali gibi kokan veri biçimini kontrol eder.

Düzenle / Güncelle: API'nın kendisinden kullanıcıdan DRY'yi ihlal etmesini istediğini de gösterir, çünkü API'yi her kullandıklarında aynı adımları tekrarlamaları gerekir .

G / Ç işlemlerinin veri işlemlerinden ayrı olduğu alternatif bir API'yi düşünün. ve API'nin kendisinin siparişe 'sahip olduğu':

ContentBuilder

SetHeader( ... )
AddLine( ... )
SetTrailer ( ... )

FileWriter

Open(filename) 
Write(content) throws InvalidContentException
Close()

Yukarıdaki ayırma ile, ContentBuilderaslında satır / başlık / römork depolamak dışında bir şey "yapmak" gerekmez (Belki de ContentBuilder.Serialize()siparişi bilen bir yöntem). Diğer SOLID ilkelerini izleyerek, artık satır eklemeden önce veya sonra üstbilgiyi veya treyleri ayarlamanız önemli değildir, çünkü içindeki hiçbir şey ContentBuilderiletilene kadar dosyaya yazılmaz FileWriter.Write.

Ayrıca biraz daha esnek olma avantajına sahiptir; örneğin, içeriği bir teşhis günlüğüne yazmak veya doğrudan bir dosyaya yazmak yerine bir ağ üzerinden geçirmek yararlı olabilir.

Bir API tasarlarken, bunun bir durum, bir dönüş değeri, bir istisna, geri arama veya başka bir şey olup olmadığını da rapor etmeyi düşünmelisiniz. API kullanıcısı muhtemelen sözleşmelerinin ihlallerini veya dosya G / Ç hataları gibi kontrol edemediği diğer hataları programlı olarak tespit edebilecek.


Tam olarak aradığım şey - teşekkürler! ISS makalesinden: "(ISS) hiçbir müşterinin kullanmadığı yöntemlere bağlı kalmaya zorlanmaması gerektiğini" belirtiyor
Robbie Dee

5
Bu kötü bir cevap değil, yine de içerik oluşturucu, çağrıların sırasının SetHeaderveya AddLineönemli olduğu bir şekilde uygulanabilir . Bu sipariş bağımlılığını ortadan kaldırmak için ne ISS ne de SRP değil, sadece POLA'dır.
Doc Brown

Sipariş önemli olduğunda, daha sonraki adımların gerçekleştirilmesi önceki adımlardan döndürülen bir değer gerektirecek ve böylece tür sistemiyle sipariş uygulanacak şekilde işlemleri tanımlayarak POLA'yı tatmin edebilirsiniz. FileWriterbu durumda, tüm girdi içeriğinin tamamlandığından emin olmak ContentBuilderiçin Writeyöntemin son adımından itibaren değer InvalidContentExceptiongerekli olabilir.
Dan Lyons

@DanLyons Askerlerin kaçınmaya çalıştığı duruma oldukça yakın hissediyorum; nerede kullanıcı API ihtiyaçlarının bilmek veya sipariş umurumda. İdeal olarak, API'nin kendisi siparişi zorlamalıdır, aksi takdirde kullanıcıdan DRY'yi ihlal etmesini ister. Ayrılmanın ContentBuilderve FileWriter.Writebu bilgi parçacığının kapsüllenmesine izin vermenin nedeni budur . İçerikle ilgili herhangi bir şeyin yanlış olması durumunda istisna gerekli olacaktır (örneğin, eksik bir başlık gibi). Bir dönüş de işe yarayabilir, ancak istisnaları dönüş kodlarına dönüştürmenin hayranı değilim.
Ben Cottrell

Ama kesinlikle KURU hakkında daha fazla not eklemeye ve cevaba sipariş vermeye değer.
Ben Cottrell

12

Bu sadece POLA ile ilgili değil, aynı zamanda olası bir hata kaynağı olarak geçersiz durumun önlenmesi ile de ilgilidir.

Somut bir uygulama sağlamadan örneğinize nasıl bazı kısıtlamalar sağlayabileceğimizi görelim:

İlk adım: Bir dosya açılmadan önce hiçbir şeyin çağrılmasına izin verme.

CreateDataFileInterface
  + OpenFile(filename : string) : DataFileInterface

DataFileInterface
  + SetHeaderString(header : string) : void
  + WriteDataLine(data : string) : void
  + SetTrailerString(trailer : string) : void
  + Close() : void

Şimdi , gerçek verilerin yazılabileceği CreateDataFileInterface.OpenFilebir DataFileInterfaceörneği almak için çağrılması gerektiği açıktır .

İkinci adım: Başlıkların ve römorkların daima ayarlandığından emin olun.

CreateDataFileInterface
  + OpenFile(filename : string, header: string, trailer : string) : DataFileInterface

DataFileInterface
  + WriteDataLine(data : string) : void
  + Close() : void

Şimdi bir DataFileInterfacedosya adı, başlık ve fragman almak için gerekli tüm parametreleri önceden sağlamanız gerekir . Tüm satırlar yazılana kadar trey dizesi kullanılamıyorsa, bu parametreyi Close()(büyük olasılıkla yöntemi yeniden adlandırmak WriteTrailerAndClose()) olarak da taşıyabilirsiniz ; böylece dosyanın en azından bir trey dizesi olmadan tamamlanması mümkün olmaz.


Yorumu yanıtlamak için:

Arayüzün ayrılmasını seviyorum. Ancak, yaptırımla ilgili önerilerinizin (ör. WriteTrailerAndClose ()) SRP ihlali konusunda kararsız olduğunu düşünmeye meyilliyim. (Bu, birkaç kez uğraştığım bir şey, ancak öneriniz olası bir örnek gibi görünüyor.) Nasıl cevap verirdiniz?

Doğru. Demek istediğim daha fazla örnek üzerinde durmak istemedim, ama bu iyi bir soru. Bu durumda bence onu arayacağım Finalize(trailer)ve çok fazla bir şey yapmadığını iddia ediyorum . Fragmanı yazmak ve kapatmak sadece uygulama detaylarıdır. Ancak, farklı olduğu konusunda aynı fikirde değilseniz veya benzer bir duruma sahipseniz, olası bir çözüm şudur:

CreateDataFileInterface
  + OpenFile(filename : string, header : string) : IncompleteDataFileInterface

IncompleteDataFileInterface
  + WriteDataLine(data : string) : void
  + FinalizeWithTrailer(trailer : string) : CompleteDataFileInterface

CompleteDataFileInterface
  + Close()

Aslında bu örnek için yapmazdım ama sonuçta tekniğin nasıl taşınacağını gösterir.

Bu arada, örneğin sırayla birçok satır yazmak için yöntemlerin aslında bu sırayla çağrılması gerektiğini varsaydım. Bu gerekli değilse, Ben Cottrel'in önerdiği gibi her zaman bir inşaatçı tercih ederim .


1
Tuzağa düştüğünüz ne yazık ki, başlangıçtan kaçınmanız için sizi açıkça uyarmıştım. Bir dosya adı gerekli değildir - başlık ve fragman da yoktur. Ancak arayüzü bölmenin genel teması iyi bir tema +1 :-)
Robbie Dee

Oh, o zaman seni yanlış anladım, bunun uygulamanın değil kullanıcının niyetini tanımladığını düşündüm.
Fabian Schmengler

Arayüzün ayrılmasını seviyorum. Ancak, yaptırım (örneğin WriteTrailerAndClose()) hakkındaki önerinizin SRP ihlali konusunda kararsız olduğunu düşünmeye meyilliyim . (Bu, birkaç kez uğraştığım bir şey, ancak öneriniz olası bir örnek gibi görünüyor.) Nasıl cevap verirdiniz?
kmote

1
@kmote cevabı bir yorum için çok uzundu, güncellememe bak
Fabian Schmengler

1
Dosya adı isteğe bağlıysa, OpenFilegerektirmeyen bir aşırı yük sağlayabilirsiniz .
5gon12eder
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.