Yerel değişkenleri örnek değişkenler yerine tercih etmenin gerekçesi?


109

Üzerinde çalıştığım kod temeli, çeşitli önemsiz yöntemler arasında veri paylaşmak için sık sık örnek değişkenleri kullanır. Asıl geliştirici, bunun Bob Amca / Robert Martin tarafından Temiz Kod kitabında belirtilen en iyi uygulamalara uyması konusunda gayretlidir : "İşlevlerin ilk kuralı, küçük olmaları gerektiğidir." ve "Bir fonksiyon için ideal argüman sayısı sıfırdır (niladik). (...) Argümanlar zordur. Çok fazla kavramsal güç harcarlar."

Bir örnek:

public class SomeBusinessProcess {
  @Inject private Router router;
  @Inject private ServiceClient serviceClient;
  @Inject private CryptoService cryptoService;

  private byte[] encodedData;
  private EncryptionInfo encryptionInfo;
  private EncryptedObject payloadOfResponse;
  private URI destinationURI;

  public EncryptedResponse process(EncryptedRequest encryptedRequest) {
    checkNotNull(encryptedRequest);

    getEncodedData(encryptedRequest);
    getEncryptionInfo();
    getDestinationURI();
    passRequestToServiceClient();

    return cryptoService.encryptResponse(payloadOfResponse);
  }

  private void getEncodedData(EncryptedRequest encryptedRequest) {
    encodedData = cryptoService.decryptRequest(encryptedRequest, byte[].class);
  }

  private void getEncryptionInfo() {
    encryptionInfo = cryptoService.getEncryptionInfoForDefaultClient();
  }

  private void getDestinationURI() {
    destinationURI = router.getDestination().getUri();
  }

  private void passRequestToServiceClient() {
    payloadOfResponse = serviceClient.handle(destinationURI, encodedData, encryptionInfo);
  }
}

Yerel değişkenleri kullanarak aşağıdakileri dikkate alın:

public class SomeBusinessProcess {
  @Inject private Router router;
  @Inject private ServiceClient serviceClient;
  @Inject private CryptoService cryptoService;

  public EncryptedResponse process(EncryptedRequest encryptedRequest) {
    checkNotNull(encryptedRequest);

    byte[] encodedData = cryptoService.decryptRequest(encryptedRequest, byte[].class);
    EncryptionInfo encryptionInfo = cryptoService.getEncryptionInfoForDefaultClient();
    URI destinationURI = router.getDestination().getUri();
    EncryptedObject payloadOfResponse = serviceClient.handle(destinationURI, encodedData,
      encryptionInfo);

    return cryptoService.encryptResponse(payloadOfResponse);
  }
}

Bu daha kısadır, çeşitli önemsiz yöntemler arasındaki örtük veri bağlantısını ortadan kaldırır ve değişken kapsamları gereken minimum ile sınırlar. Yine de, bu faydalara rağmen, orijinal geliştiriciyi yukarıda belirtilen Bob Amca'nın uygulamalarıyla çelişiyor gibi göründüğü için bu yeniden düzenlemenin garantili olduğuna ikna edemiyorum.

Dolayısıyla benim sorularım: Örnek değişkenler yerine yerel değişkenleri desteklemenin amacı, bilimsel mantığı nedir? Parmağımı üstüne koymuş gibi görünemiyorum. Benim sezgi gizli kaplinler kötü ve dar bir kapsam geniş olandan daha iyi olduğunu gösteriyor bana. Fakat bunu destekleyen bilim nedir?

Ve tam tersine, bu yeniden gözden geçirmenin muhtemelen göz ardı ettiğim herhangi bir olumsuz tarafı var mı?


Yorumlar uzun tartışmalar için değildir; bu konuşma sohbete taşındı .
maple_shaft

Yanıtlar:


170

Örnek değişkenler yerine yerel değişkenleri desteklemenin amacı, bilimsel mantığı nedir?

Kapsam bir ikili durum değil, bir degrade. Bunları en büyükten en küçüğe doğru sıralayabilirsiniz:

Global > Class > Local (method) > Local (code block, e.g. if, for, ...)

Düzenleme: "sınıf kapsamı" dediğim şey, "örnek değişkeni" ile kastettiğiniz şeydir. Bildiğim kadarıyla, bunlar eşanlamlı, ama ben bir C # dev değilim, bir Java dev değil. Kısacası uğruna, tüm istatistikleri statik kategoriye dahil ettim çünkü statikler sorunun konusu değil.

Kapsam ne kadar küçükse, o kadar iyidir. Bunun nedeni, değişkenlerin mümkün olan en küçük kapsamda yaşaması gerektiğidir . Bunun birçok faydası var:

  • Sizi mevcut sınıfın sorumluluğu hakkında düşünmeye zorlar ve SRP'ye bağlı kalmanıza yardımcı olur.
  • Bu örneğin küresel bir ad çakışmasını, iki veya daha fazla sınıfları bir varsa kaçınmak zorunda değilsiniz sağlayan Nameözelliği, sen gibi onları önüne zorunda değiliz FooName, BarName... Böylece mümkün olduğunca temiz ve veciz sizin değişken adları tutmak.
  • Kullanılabilir değişkenleri (örneğin, Intellisense için) bağlamsal olarak alakalı olanlarla sınırlandırarak kodu çözer.
  • Bir çeşit erişim kontrolü sağlar, böylece verileriniz bilmediğiniz bir aktör tarafından manipüle edilemez (örneğin, bir meslektaşım tarafından geliştirilen farklı bir sınıf).
  • Bu değişkenlerin bildiriminin, bu değişkenlerin fiili kullanımına mümkün olduğu kadar yakın kalmaya çalıştığından, kodu daha okunaklı hale getirir.
  • Değişkenleri aşırı geniş bir kapsamda açıkça bildirmek, genellikle OOP'yi tam olarak anlamayan bir geliştiricinin veya bunun nasıl uygulanacağının bir göstergesidir. Aşırı geniş kapsamlı değişkenleri görmek, OOP yaklaşımında (genel olarak geliştirici veya özel olarak kodbase ile) büyük olasılıkla yanlış giden bir şey olduğunu kırmızı bayrak olarak görür.
  • (Yorum yapan Kevin) Yerel halkın kullanılması, işleri doğru sırayla yapmaya zorlar. Orijinal (sınıf değişkeni) kodunda, yanlış bir passRequestToServiceClient()şekilde yöntemin en üstüne gidebilirsiniz ve yine de derlenir. Yerlilerle, bu hatayı ancak başlatılmamış bir değişkeni geçtiyseniz yapabilirsiniz; bu, gerçekten yapmamanız için yeterince açıktır.

Yine de, bu faydalara rağmen, orijinal geliştiriciyi yukarıda belirtilen Bob Amca'nın uygulamalarıyla çelişiyor gibi göründüğü için bu yeniden düzenlemenin garantili olduğuna ikna edemiyorum.

Ve tam tersine, bu yeniden gözden geçirmenin muhtemelen göz ardı ettiğim herhangi bir olumsuz tarafı var mı?

Buradaki sorun, yerel değişkenler için yaptığınız argümanın geçerli olması, ancak aynı zamanda doğru olmayan ve önerilen düzeltmenizin koku testinde başarısız olmasına neden olan ek değişiklikler yaptınız.

"Sınıf değişkeni yok" önerinizi anlıyorum ve buna değecek bir şey olsa da, aslında yöntemleri de kaldırdınız ve bu tamamen farklı bir oyun adı. Yöntemler kalmalı ve bunun yerine bir sınıf değişkeninde saklamak yerine değerlerini döndürecek şekilde değiştirmelisiniz:

private byte[] getEncodedData() {
    return cryptoService.decryptRequest(encryptedRequest, byte[].class);
}

private EncryptionInfo getEncryptionInfo() {
    return cryptoService.getEncryptionInfoForDefaultClient();
}

// and so on...

Bu processyöntemde ne yaptığınıza katılıyorum , ancak vücutlarını doğrudan uygulamak yerine özel alt metotları çağırıyor olmalısınız.

public EncryptedResponse process(EncryptedRequest encryptedRequest) {
    checkNotNull(encryptedRequest);

    byte[] encodedData = getEncodedData();
    EncryptionInfo encryptionInfo = getEncryptionInfo();

    //and so on...

    return cryptoService.encryptResponse(payloadOfResponse);
}

Bu ekstra soyutlama katmanını, özellikle de birkaç kez tekrar kullanılması gereken yöntemlerle karşılaştığınızda istersiniz. Halen yöntemlerinizi tekrar kullanmasanız bile, sadece kod okunabilirliğine yardımcı olsa bile, ilgili durumlarda alt yöntemler oluşturmak için hala iyi bir uygulama meselesidir.

Yerel değişken argümanından bağımsız olarak, derhal önerilen düzeltmenizin orijinalinden daha az okunabilir olduğunu fark ettim. Sınıf değişkenlerinin wanton kullanımının da kod okunabilirliğinden saptığını kabul ediyorum;


Yorumlar uzun tartışmalar için değildir; bu konuşma sohbete taşındı .
maple_shaft

79

Orijinal kod, argümanlar gibi üye değişkenleri kullanıyor. Argüman sayısını en aza indirmeyi söylediğinde, gerçekte ne demek istediği, işlevlerini yerine getirmek için yöntemlerin gerektirdiği veri miktarını en aza indirmektir. Bu verileri üye değişkenlerine koymak hiçbir şeyi geliştirmez.


20
Tamamen katılıyorum! Bu üye değişkenleri basitçe örtük işlev argümanlarıdır. Aslında, bu değişkenlerin değeri ile fonksiyon kullanımı arasında (harici bir POV'den) açık bir bağlantı olmadığı için daha da kötüsü
Rémi

1
Bunun kitabın kastettiği olmadığını söyleyebilirim. Kaç tane fonksiyonun çalışması için tam olarak sıfır giriş verisi gerekir? Bu kitabın yanlış olduğunu düşünüyorum.
Qwertie

1
@Qwertie Bir nesneniz varsa, üzerinde çalıştığı veriler tamamen içinde saklanabilir. Gibi işlevler çok tuhaf görünmemeli process.Start();veya myString.ToLowerCase()olmamalıdır (ve gerçekten anlaşılması en kolay olanıdır).
R. Schmitz

5
Her ikisinin de bir argümanı vardır: örtük this. Biri bile bu argümanın açıkça verildiğini iddia edebilir - noktadan önce.
BlackJack

47

Diğer cevaplar, yerel değişkenlerin faydalarını zaten mükemmel bir şekilde açıkladı, geriye kalan tek şey sorunuzun bu kısmı:

Yine de, bu faydalara rağmen, orijinal geliştiriciyi yukarıda belirtilen Bob Amca'nın uygulamalarıyla çelişiyor gibi göründüğü için bu yeniden düzenlemenin garantili olduğuna ikna edemiyorum.

Bu kolay olmalı. Bob Amca'nın Temiz Kodunda şu alıntıyı göster:

Yan Etkisi Olmaz

Yan etkileri yalan. İşleviniz bir şey yapmayı vaat ediyor ama aynı zamanda başka gizli şeyler de yapıyor. Bazen kendi sınıfının değişkenlerinde beklenmeyen değişiklikler yapacaktır. Bazen onları fonksiyona geçirilen parametrelere ya da sistem glob'larına yönlendirir. Her iki durumda da, garip zamansal eşleşmelere ve düzen bağımlılıklarına neden olan doyurucu ve zarar verici buğulanmalardır.

(örnek atlandı)

Bu yan etki geçici bir eşleşme yaratır. Yani, checkPassword yalnızca belirli zamanlarda (başka bir deyişle, oturumu başlatmak güvenli olduğunda) çağrılabilir. Sıra dışı çağrılırsa, oturum verileri istemeden kaybolabilir. Geçici kaplinler, özellikle bir yan etki olarak gizlendiğinde kafa karıştırıcıdır. Geçici bir kaplin olması gerekiyorsa, fonksiyon adına açıkça belirtmelisiniz. Bu durumda checkPasswordAndInitializeSession işlevini yeniden adlandırabiliriz, ancak bu kesinlikle bir şeyi yapın.

Yani, Bob Amca sadece bir fonksiyonun birkaç argüman alması gerektiğini söylemez, ayrıca fonksiyonların mümkün olduğunda yerel olmayan durumlarla etkileşime girmekten kaçınması gerektiğini söyler.


3
Bir "kaymakam dünyasında", bu listelenen ikinci cevap olacaktır. İş arkadaşınızın mantığa dinlediği ideal durum için ilk cevap - ancak iş arkadaşı bir coşkunsa, bu cevap çok fazla bir sorun olmadan durumla ilgilenir.
R. Schmitz

2
Bu fikre daha pragmatik bir varyasyon için, yerel devlet hakkında hak iddia etmenin örnek veya küresel devletten çok daha kolay olması. İyi tanımlanmış ve sıkı bir şekilde değişkenlik ve değişkenlik ve yan etkiler nadiren sorunlara yol açar. Örneğin, birçok sıralama işlevi yan etki yoluyla yerinde çalışır, ancak bunun yerel bir kapsamda düşünmesi kolaydır.
Beefster

1
Ah, eski güzel "çelişkili bir aksiyomatik, her şeyi kanıtlamak mümkündür". Gerçek gerçekler olmadığı ve IRL'ye yalan söyleyemediği için, herhangi bir dogmanın zıt şeyleri ifade eden ifadeleri içermesi gerekir, yani çelişkili.
ivan_pozdeev

26

“Birisinin amcasının ne düşündüğü ile çelişiyor” ASLA ASLA iyi bir tartışma değildir. ASLA. Amcalardan bilgelik alma, kendin için düşün.

Bununla birlikte, örnek değişkenler, aslında kalıcı veya yarı kalıcı olarak saklanması gereken bilgileri depolamak için kullanılmalıdır. Buradaki bilgi değil. Örnek değişkenleri olmadan yaşamak çok basittir, böylece gidebilirler.

Test: Her bir örnek değişkeni için bir dokümantasyon yorumu yazın. Tamamen anlamsız olmayan bir şey yazabilir misin? Ve dört erişimciye bir dokümantasyon yorumu yazınız. Onlar eşit derecede anlamsız.

En kötüsü, farklı bir cryptoService kullandığınız için değişikliklerin şifresini çözmenin yolunu farz edin. Dört kod satırını değiştirmek yerine, dört örnek değişkenini farklı olanlarla, dört alıcıyı farklı olanlarla değiştirmeniz ve dört satırlı kod satırını değiştirmeniz gerekir.

Ancak kod satırına göre ödeme yapmanız durumunda elbette ilk sürüm tercih edilir. 11 satır yerine 31 satır. Yazmak ve sonsuza kadar sürdürmek için üç kat daha fazla satır yazmak, bir şeyleri ayıklarken okumaya devam etmek, değişiklik gerektiğinde uyum sağlamak, ikinci bir cryptoService özelliğini destekliyorsanız kopyalamak için.

(Yerel değişkenleri kullanmanın sizi aramaları doğru sıraya koymaya zorladığı önemli noktayı kaçırdık).


16
Kendin için düşünmek elbette iyidir. Ancak açılış paragrafınız etkili bir şekilde öğretmenlerin veya yaşlıların girdilerini reddeden öğrenciler veya gençler içerir; Bu çok ileri gidiyor.
Mart’ta

9
@Flater Öğretmenlerin veya yaşlıların girdileri hakkında düşündükten ve yanlış olduklarını gördükten sonra, girişlerini reddetmek tek doğru şeydir. Sonunda, meslekten vazgeçmek değil, onu sorgulamak ve sadece kesinlikle yanlış olduğunu ispatlamakla işten çıkarmakla ilgilidir.
glglgl

10
@HristianHackl: Ben gelenekselliği takip etmeden, tam olarak gemideyim ama ben de görmezden gelmeyeceğim. Yanıt, görünüşe göre edinilen bilgiyi kendi görüşünüze göre değerlendirmenizi öneriyor ve bu, alınması gereken sağlıklı bir yaklaşım değil. Diğerlerinin görüşlerini körü körüne takip etmek, hayır. Anlaşamadığınızda bir şeyi sorgulamak, tabii ki evet. Tamamen reddediyorum çünkü aynı fikirde değilsin, hayır. Okuduğum gibi, cevabın ilk paragrafı en azından ikincisini öne sürüyor gibi görünüyor . Bu, gnasher'ın "kendiniz için düşünün" ile ne anlama geldiğine ve bunun için de detaylandırmaya ihtiyacı olduğuna bağlı.
flater

9
Bir paylaşım bilgelik platformunda, bu biraz yer dışına görünüyor ...
drjpizzle

4
ASLA ASLA ASLA iyi bir argüman değildir ... Kurallara uymak için kuralların var olduğuna tamamen katılıyorum. Ancak, kurallarla ilgili kurallar kuralların bir alt sınıfıdır, bu yüzden kendi kararınızı
verdiniz

14

Örnek değişkenler yerine yerel değişkenleri desteklemenin amacı, bilimsel mantığı nedir? Parmağımı üstüne koymuş gibi görünemiyorum. Sezgim, bana gizli kaplinlerin kötü olduğunu ve dar bir kapsamın geniş olandan daha iyi olduğunu söylüyor. Fakat bunu destekleyen bilim nedir?

Örnek değişkenleri , nesnenin kendisinden daha dar kapsamlı olan hesaplama ipliklerine özgü özellikleri göstermek için değil , ana bilgisayar nesnelerinin özelliklerini temsil etmek içindir. Daha önce ele alınmamış gibi görünen bir ayrım çizmenin sebepleri, eşzamanlılık ve geri dönme çevresinde döner. Yöntemler, örnek değişkenlerin değerlerini ayarlayarak veri alışverişinde bulunursa, iki eşzamanlı iş parçacığı, birbirinin değerlerini bu örnek değişkenleri için kolaylıkla kesintiye uğratabilir, aralıklı, bulunması zor hatalar verir.

Yalnızca bir iş parçacığı bile bu satırlar boyunca sorunlarla karşılaşabilir, çünkü örnek değişkenlere dayanan bir veri alışverişi modelinin önemsiz yöntemleri yapma riski vardır. Benzer şekilde, aynı değişkenler farklı yöntem çiftleri arasında veri iletmek için kullanılıyorsa, özyinelemeli olmayan bir yöntem çağırma zincirini bile yürüten tek bir dişlinin, söz konusu örnek değişkenlerin beklenmedik modifikasyonları etrafında dönen böceklerle karşılaşması riski vardır.

Böyle bir senaryoda güvenilir bir şekilde doğru sonuçlar almak için, birinin diğerini çağırdığı her bir yöntem çifti arasında iletişim kurmak için ayrı değişkenler kullanmanız veya başka bir uygulamanın diğerlerinin tüm uygulama detaylarını hesaba katması için başka değişkenler kullanmanız gerekir. ister doğrudan ister dolaylı olsun, onu çağırdığı yöntemler. Bu kırılgan ve zayıf ölçeklenir.


7
Şimdiye kadar, iş güvenliği ve eşzamanlılıktan bahseden tek cevap bu. Bu sorudaki belirli kod örneğine göre şaşırtıcı bir durum: SomeBusinessProcess örneği bir defada birden fazla şifreli isteği güvenli bir şekilde işleyemez. Yöntem public EncryptedResponse process(EncryptedRequest encryptedRequest)senkronize edilmedi ve eşzamanlı çağrılar büyük olasılıkla örnek değişkenlerin değerlerini engelliyor. Bu ortaya çıkarmak için iyi bir nokta.
Joshua Taylor

9

Sadece tartışırken process(...), meslektaşlarınızdan örnek, iş mantığı anlamında çok daha okunaklıdır. Buna karşılık, karşı örneğiniz, herhangi bir anlam çıkarmak için elverişli bir bakıştan daha fazlasını gerektirir.

Söylendiği gibi, temiz kod hem okunaklı hem de kaliteli - yerel durumu daha küresel bir alana sokmak sadece üst düzey bir montaj, yani kalite için sıfır.

public class SomeBusinessProcess {
  @Inject private Router router;
  @Inject private ServiceClient serviceClient;
  @Inject private CryptoService cryptoService;

  public EncryptedResponse process(EncryptedRequest request) {
    checkNotNull(encryptedRequest);

    return encryptResponse
      (routeTo
         ( destination()
         , requestData(request)
         , destinationEncryption()
         )
      );
  }

  private byte[] requestData(EncryptedRequest encryptedRequest) {
    return cryptoService.decryptRequest(encryptedRequest, byte[].class);
  }

  private EncryptionInfo destinationEncryption() {
    return cryptoService.getEncryptionInfoForDefaultClient();
  }

  private URI destination() {
    return router.getDestination().getUri();
  }

  private EncryptedObject routeTo(URI destinationURI, byte[] encodedData, EncryptionInfo encryptionInfo) {
    return serviceClient.handle(destinationURI, encodedData, encryptionInfo);
  }

  private void encryptResponse(EncryptedObject payloadOfResponse) {
    return cryptoService.encryptResponse(payloadOfResponse);
  }
}

Bu, herhangi bir kapsamdaki değişkenlere olan ihtiyacı gideren bir yorumlamadır. Evet, derleyici bunları üretecektir, ancak önemli olan, kodun etkin olacağını kontrol etmektir. Aynı zamanda nispeten okunaklı olmakla birlikte.

Adlandırmada sadece bir nokta. Zaten mevcut olan bilgilerde anlamlı ve genişleyen en kısa adı istersiniz. yani. destinationURI, 'URI', tür imzası tarafından zaten biliniyor.


4
Tüm değişkenleri ortadan kaldırmak mutlaka kodun okunmasını kolaylaştırmaz.
Pharap

Pointfree tarzı ile tüm değişkenleri tamamen ortadan kaldırın en.wikipedia.org/wiki/Tacit_programming
Marcin

@Pharap Doğru, değişkenlerin eksikliği okunabilirliği sağlamaz. Bazı durumlarda hata ayıklamayı daha da zorlaştırır. Mesele şu ki, iyi seçilmiş isimler, açık bir ifade kullanımı, hala etkili olsa da, bir fikri çok net bir şekilde aktarabiliyor.
Kain0_0

7

Ben sadece bu değişkenleri ve özel yöntemleri tamamen kaldırırdım. İşte benim refactor:

public class SomeBusinessProcess {
  @Inject private Router router;
  @Inject private ServiceClient serviceClient;
  @Inject private CryptoService cryptoService;

  public EncryptedResponse process(EncryptedRequest encryptedRequest) {
    return cryptoService.encryptResponse(
        serviceClient.handle(router.getDestination().getUri(),
        cryptoService.decryptRequest(encryptedRequest, byte[].class),
        cryptoService.getEncryptionInfoForDefaultClient()));
  }
}

Özel yöntem için, örneğin router.getDestination().getUri(), bundan daha net ve okunaklıdır getDestinationURI(). Aynı çizgiyi aynı sınıfta iki kez kullanırsam bunu tekrar ediyorum. Başka bir açıdan bakmak için, eğer bir ihtiyaç varsa getDestinationURI(), o zaman muhtemelen sınıfta değil, başka bir sınıfa aittir SomeBusinessProcess.

Değişkenler ve özellikler için ortak ihtiyaç, zaman içinde kullanılacak değerleri tutmaktır. Sınıfın özellikler için ortak bir arayüzü yoksa, muhtemelen özellikler olmamalıdır. Kullanılan sınıf özelliklerinin en kötüsü, muhtemelen yan etkiler yoluyla özel yöntemler arasında değerleri geçmek içindir.

Her neyse, sınıf sadece yapmalı process()ve sonra nesne atılacak, herhangi bir devleti bellekte tutmaya gerek kalmayacak. Diğer refactor potansiyeli, CryptoService'i bu sınıftan çıkarmak olacaktır.

Yorumlara dayanarak, bu cevabı gerçek dünya pratiklerine dayanarak eklemek istiyorum. Nitekim, kod incelemesinde seçeceğim ilk şey, sınıfı yeniden alevlendirmek ve şifreleme / şifre çözme çalışmalarını yürütmektir. Bu yapıldıktan sonra, yöntemlere ve değişkenlere ihtiyaç duyulup duyulmadığını, doğru isimlendirildiklerini vb. Son kod muhtemelen buna daha yakın olacaktır:

public class SomeBusinessProcess {
  @Inject private Router router;
  @Inject private ServiceClient serviceClient;

  public Response process(Request request) {
    return serviceClient.handle(router.getDestination().getUri());
  }
}

Yukarıdaki kod ile, daha fazla refactor ihtiyacı olduğunu sanmıyorum. Kurallarda olduğu gibi, bunları ne zaman ve ne zaman uygulayamayacağınızı bilmek deneyim ister. Kurallar her durumda işe yaradığı kanıtlanmış teoriler değildir.

Öte yandan, kod incelemesi, bir kod parçasının geçilmesinden ne kadar süre önce gerçek bir etkiye sahiptir. Benim numaram daha az koda sahip olmak ve anlaşılmasını kolaylaştırmak. Değişken ismi bir tartışma konusu olabilir, eğer onu kaldırabilirsem, düşünmek bile zorunda kalmayacağım.


Olumlu oyum olsa da, çoğu burada tereddüt edecek. Tabii bazı soyutlamalar, mantıklı. (BTW "işlem" olarak adlandırılan bir yöntem korkunçtur.) Fakat burada mantık çok az. Ancak OP'nin sorusu tüm kod stilinde ve durum daha da karmaşık olabilir.
Joop Eggen

1
Hepsini tek bir yöntem çağrısında zincirlemeyle ilgili net bir konu, tamamen okunabilirliktir. Ayrıca, belirli bir nesne üzerinde birden fazla işleme ihtiyaç duyduğunuzda da çalışmaz. Ayrıca, bu işlem hata ayıklamak imkansızdır çünkü işlemler arasında adım atamaz ve nesneleri inceleyemezsiniz. Bu teknik düzeyde çalışırken, yazılım geliştirmenin çalışma zamanı olmayan yönlerini büyük ölçüde ihmal ettiği için bunun için savunuculuk yapmayacağım.
flater

@Flater Yorumlarınıza katılıyorum, bunu her yere uygulamak istemiyoruz. Pratik pozisyonumu açıklığa kavuşturmak için cevabımı düzenledim. Göstermek istediğim şey pratikte sadece uygun olduğunda kurallar uyguladığımızdır. Zincirleme yöntem çağrısı bu durumda gayet iyi ve hata ayıklamam gerekirse, zincirleme yöntemleri için testleri çağırabilirim.
imel96

@ JoopEggen Evet, soyutlamalar mantıklı. Örnekte, özel yöntemler zaten soyutlama yapmıyor, sınıftaki kullanıcılar onlar hakkında hiçbir şey bilmiyorlar
imel96

1
@ imel96, ServiceClient ve CryptoService arasındaki eşleştirmenin, SBP yerine SC'yi CS'ye enjekte etmeye odaklanmayı, temel problemi daha yüksek mimari düzeyde çözmeyi ... bu IMVHO bu hikayenin ana noktası; ayrıntılara odaklanırken büyük resmin izini sürmek çok kolaydır.
vaxquis

4

Flater'ın cevabı oldukça iyi kapsam belirleme konularını kapsar, ancak burada da başka bir konu olduğunu düşünüyorum.

Verileri işleyen bir işlev ile yalnızca verilere erişen bir işlev arasında bir fark olduğunu unutmayın .

İlki gerçek iş mantığını yürütür, ikincisi ise yazarak tasarruf sağlar ve belki de daha basit ve tekrar kullanılabilir bir arayüz ekleyerek güvenlik sağlar.

Bu durumda, veri erişim fonksiyonlarının yazmadan tasarruf etmediği ve herhangi bir yerde tekrar kullanılmadığı (veya bunların kaldırılmasında başka sorunlar olabileceği) görülmektedir. Yani bu fonksiyonlar basitçe olmamalı.

Sadece iş mantığını isimlendirilmiş fonksiyonlarda tutarak, her iki dünyanın da en iyisini elde ediyoruz ( Flater'in cevabı ile imel96'nın cevabı arasında ).

public EncryptedResponse process(EncryptedRequest encryptedRequest) {

    byte[] requestData = decryptRequest(encryptedRequest);
    EncryptedObject responseData = handleRequest(router.getDestination().getUri(), requestData, cryptoService.getEncryptionInfoForDefaultClient());
    EncryptedResponse response = encryptResponse(responseData);

    return response;
}

// define: decryptRequest(), handleRequest(), encryptResponse()

3

İlk ve en önemli şey: Bob Amca bazen bir vaiz gibi görünüyor, ancak kurallarında istisnalar olduğunu belirtiyor.

Temiz Kod fikri, okunabilirliği artırmak ve hatalardan kaçınmaktır. Birbirini ihlal eden birkaç kural var.

İşlevler hakkındaki argümanı, niladik işlevlerin en iyisi olduğu, ancak üç parametreye kadar kabul edilebilir olduğu yönündedir. Şahsen ben 4 de iyi olduğunu düşünüyorum.

Örnek değişkenleri kullanıldığında, tutarlı bir sınıf oluşturmaları gerekir. Bunun anlamı, statik olmayan yöntemlerin tümü değilse de değişkenlerin birçoğunda kullanılması gerektiğidir.

Sınıfın pek çok yerinde kullanılmayan değişkenler taşınmalıdır.

Ne orjinali ne de refactored versiyonunun optimal olduğunu düşünmezdim ve @Flater zaten geri dönüş değerleri ile neler yapılabileceğini çok iyi ifade etti. Okunabilirliği arttırır ve dönüş değerlerini kullanmak için hataları azaltır.


1

Yerel değişkenler kapsamı azaltır, dolayısıyla değişkenlerin kullanılabileceği yolları sınırlandırır ve bu nedenle belirli hata sınıflarını önlemeye yardımcı olur ve okunabilirliği artırır.

Örnek değişkeni, belirli hata sınıflarını azaltmaya yardımcı olan ve okunabilirliği artıran, fonksiyonun çağrılma yöntemlerini azaltır.

Birinin doğru, diğerinin yanlış olduğunu söylemek, belirli bir durumda geçerli bir sonuç olabilir, ancak genel tavsiye olarak ...

TL; DR: Bence çok fazla coşku duyma sebebiniz, çok fazla coşku var.


0

Başlamak için… yöntemlerinin boşluğa geri dönmemesi gerektiğine rağmen, ilk çözümde yöntemlerdeki soyutlama seviyelerinin ayrılması verilmiştir. İkinci çözüm daha kapsamlı olsa da, yöntemde neler olup bittiğine dair aklınıza gelmek yine de daha zordur. Yerel değişkenlerin ataması burada gerekli değildir. Metot adlarını saklar ve kodunu şu şekilde değiştiririm:

public class SomeBusinessProcess {
  @Inject private Router router;
  @Inject private ServiceClient serviceClient;
  @Inject private CryptoService cryptoService;

  public EncryptedResponse process(EncryptedRequest encryptedRequest) {
    checkNotNull(encryptedRequest);

    return getEncryptedResponse(
            passRequestToServiceClient(getDestinationURI(), getEncodedData(encryptedRequest) getEncryptionInfo())
        );
  }

  private EncryptedResponse getEncryptedResponse(EncryptedObject encryptedObject) {
    return cryptoService.encryptResponse(encryptedObject);
  }

  private byte[] getEncodedData(EncryptedRequest encryptedRequest) {
    return cryptoService.decryptRequest(encryptedRequest, byte[].class);
  }

  private EncryptionInfo getEncryptionInfo() {
    return cryptoService.getEncryptionInfoForDefaultClient();
  }

  private URI getDestinationURI() {
    return router.getDestination().getUri();
  }

  private EncryptedObject passRequestToServiceClient(URI destinationURI, byte[] encodedData, EncryptionInfo encryptionInfo) {
    return serviceClient.handle(destinationURI, encodedData, encryptionInfo);
  }
}

0

Her iki şey de aynı şeyi yapıyor ve performans farkları göze çarpmıyor, bu yüzden bilimsel bir tartışma olduğunu sanmıyorum . O zaman öznel tercihlere gelir.

Ben de meslektaşınızdan daha iyi bir şekilde hoşlanma eğilimindeyim. Neden? Çünkü bazı kitap yazarlarının söylediklerine rağmen okumak ve anlamak daha kolay olduğunu düşünüyorum.

Her iki yol da aynı şeyi başarır, ancak onun yolu daha dağılmıştır. Bu kodu okumak için çeşitli fonksiyonlar ve üye değişkenleri arasında ileri geri gitmeniz gerekir. Hepsi tek bir yerde yoğunlaşmış değil, bunu anlamak için kafanızdaki her şeyi hatırlamanız gerekiyor. Çok daha büyük bir bilişsel yük.

Buna karşılık, yaklaşımınız her şeyi çok daha yoğun bir biçimde bir araya getiriyor, ancak onu olanaksız kılmak için değil. Sadece satırdan sonra satır okudunuz ve anlamak için çok fazla ezberlemenize gerek yok.

O oluyor Ancak eğer kullanılan böyle bir moda dizilme ihtimali koduna, onun için o başka bir şekilde yuvarlak olabileceğini tahmin edebilirsiniz.


Bu gerçekten de akışı anlayabilmem için büyük bir engel olduğunu kanıtladı. Bu örnek oldukça küçüktür, ancak bir diğeri bağımlı sonuçları içeren düzinelerce örnek değişkenleri saymazken, ara sonuçları için 35 (!) Örnek değişkenini kullanmıştır. Daha önce belirlenmiş olanları takip etmeniz gerektiğinden, takip etmek oldukça zordu. Hatta bazıları daha sonra tekrar kullanıldı ve daha da zorlaştı. Neyse ki burada sunulan argümanlar sonunda meslektaşımı yeniden düzenlemeye katılmaya ikna etti.
Alexander,
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.