CodeGym /Java Blogu /Rastgele /Anti-paternler nelerdir? Bazı örneklere bakalım (Bölüm 2)...
John Squirrels
Seviye
San Francisco

Anti-paternler nelerdir? Bazı örneklere bakalım (Bölüm 2)

grupta yayınlandı
Anti-paternler nelerdir? Bazı örneklere bakalım (Bölüm 1) Bugün en popüler anti-kalıpları incelememize devam ediyoruz. İlk bölümü kaçırdıysanız, işte burada . Anti-paternler nelerdir?  Bazı örneklere bakalım (2. Kısım) - 1Bu nedenle, tasarım kalıpları en iyi uygulamalardır. Başka bir deyişle, belirli sorunları çözmenin iyi, zaman içinde test edilmiş yollarının örnekleridir. Buna karşılık, anti-kalıplar, çeşitli problemleri (kötü kalıplar) çözerken tuzaklar veya hatalar kalıpları olmaları anlamında tam tersidir. Bir sonraki yazılım geliştirme anti-kalıpına geçelim.

8. Altın çekiç

Altın çekiç, belirli bir çözümün evrensel olarak uygulanabilir olduğuna duyulan güvenle tanımlanan bir anti-kalıptır. Örnekler:
  1. Bir problemle karşılaştıktan ve mükemmel çözüm için bir model bulduktan sonra, bir programcı, belirli durumlar için uygun çözümler aramak yerine, bu modeli her yere yapıştırmaya, mevcut ve gelecekteki tüm projelere uygulamaya çalışır.

  2. Bazı geliştiriciler bir zamanlar belirli bir durum için kendi önbellek varyantlarını yarattılar (çünkü başka hiçbir şey uygun değildi). Daha sonra özel bir önbellek mantığı içermeyen bir sonraki projede, hazır kitaplıklar (örneğin Ehcache) kullanmak yerine yine varyantlarını kullandılar. Sonuç, bir sürü böcek ve uyumsuzluğun yanı sıra çok fazla zaman kaybı ve kızarmış sinirlerdi.

    Herkes bu anti-kalıp için düşebilir. Yeni başlayan biriyseniz, tasarım kalıpları hakkında bilgi sahibi olmayabilirsiniz. Bu, tüm sorunları hakim olduğunuz tek yoldan çözmeye çalışmanıza yol açabilir. Profesyonellerden bahsediyorsak, buna profesyonel deformasyon veya nerdview diyoruz. Kendi tercih ettiğiniz tasarım desenleriniz var ve doğru olanı kullanmak yerine, geçmişte iyi bir uyumun gelecekte aynı sonucu garanti ettiğini varsayarak favorinizi kullanırsınız.

    Bu tuzak, kötü, istikrarsız ve sürdürülmesi zor bir uygulamadan projenin tamamen başarısız olmasına kadar çok üzücü sonuçlara yol açabilir. Tüm hastalıklar için tek bir hap olmadığı gibi, tüm durumlar için tek bir tasarım modeli de yoktur.

9. Erken optimizasyon

Erken optimizasyon , adı kendisi için konuşan bir anti-kalıptır.
"Programcılar, koddaki kritik olmayan yerler hakkında düşünmek ve endişelenmek ve bunları optimize etmek için çok fazla zaman harcıyorlar, bu da sonraki hata ayıklamayı ve desteği yalnızca olumsuz etkiler. Genel olarak, örneğin, vakaların %97'sinde optimizasyonu unutmamız gerekir. Üstelik , erken optimizasyon tüm kötülüklerin anasıdır. Bununla birlikte, tüm dikkatimizi kalan %3'e vermeliyiz." —Donald Knuth
Örneğin, bir veritabanına zamanından önce dizin eklemek. Bu neden kötü? Pekala, indekslerin ikili ağaç olarak saklanması kötü. Sonuç olarak, her yeni değer eklendiğinde ve silindiğinde, ağaç yeniden hesaplanır ve bu, kaynakları ve zamanı tüketir. Bu nedenle, dizinler yalnızca acil bir ihtiyaç olduğunda (çok miktarda veriniz varsa ve sorgulamalar çok uzun sürüyorsa) ve yalnızca en önemli alanlar için (en sık sorgulanan alanlar) eklenmelidir.

10. Spagetti kodu

Spagetti kodu, sarma istisnaları, koşullar ve döngüler gibi her türlü dallanmayı içeren, kötü yapılandırılmış, kafa karıştırıcı ve anlaşılması zor bir kod tarafından tanımlanan bir anti-kalıptır. Önceden, goto operatörü bu anti-kalıpın ana müttefikiydi. Goto deyimleri artık gerçekten kullanılmıyor, bu da ilgili bir takım zorlukları ve sorunları mutlu bir şekilde ortadan kaldırıyor.

public boolean someDifficultMethod(List<String> XMLAttrList) {
           ...
   int prefix = stringPool.getPrefixForQName(elementType);
   int elementURI;
   try {
       if (prefix == -1) {
        ...
           if (elementURI != -1) {
               stringPool.setURIForQName(...);
           }
       } else {
        ...
           if (elementURI == -1) {
           ...
           }
       }
   } catch (Exception e) {
       return false;
   }
   if (attrIndex != -1) {
       int index = attrList.getFirstAttr(attrIndex);
       while (index != -1) {
           int attName = attrList.getAttrName(index);
           if (!stringPool.equalNames(...)){
           ...
               if (attPrefix != namespacesPrefix) {
                   if (attPrefix == -1) {
                    ...
                   } else {
                       if (uri == -1) {
                       ...
                       }
                       stringPool.setURIForQName(attName, uri);
                   ...
                   }
                   if (elementDepth >= 0) {
                   ...
                   }
                   elementDepth++;
                   if (elementDepth == fElementTypeStack.length) {
                   ...
                   }
               ...
                   return contentSpecType == fCHILDRENSymbol;
               }
           }
       }
   }
}
Korkunç görünüyor, değil mi? Ne yazık ki, bu en yaygın anti-kalıp :( Bu tür bir kodu yazan kişi bile gelecekte onu anlayamayacak. Kodu gören diğer geliştiriciler, "Pekala, işe yararsa, o zaman tamam - ona dokunmamak daha iyidir". Genellikle, bir yöntem başlangıçta basit ve çok şeffaftır, ancak yeni gereksinimler eklendikçe, yöntem giderek daha fazla koşullu ifadelerle yüklenerek onu bunun gibi bir canavarlığa dönüştürür. göründüğünde, ya tamamen ya da en azından en karmaşık kısımlarını yeniden düzenlemeniz gerekir.Tipik olarak, bir proje planlanırken, yeniden düzenleme için zaman ayrılır, örneğin, sprint süresinin %30'u yeniden düzenleme ve testler içindir.Tabii ki bu, acele olmadığını (ama bu ne zaman olur).burada _

11. Sihirli sayılar

Sihirli sayılar, her türlü sabitin amaç veya anlamlarına dair herhangi bir açıklama yapılmadan bir programda kullanıldığı bir anti-kalıptır. Yani, genellikle kötü adlandırılırlar veya aşırı durumlarda, yorumların ne olduğunu veya nedenini açıklayan bir yorum yoktur. Spagetti kodu gibi, bu da en yaygın anti-kalıplardan biridir. Kodu yazmayan birinin sihirli sayılar veya nasıl çalıştıkları hakkında bir fikri olabilir veya olmayabilir (ve zamanla yazarın kendisi bunları açıklayamayacaktır). Sonuç olarak, bir sayıyı değiştirmek veya kaldırmak, kodun sihirli bir şekilde hep birlikte çalışmayı durdurmasına neden olur. Örneğin, 36 ve 73. Bu anti-kalıpla mücadele etmek için bir kod incelemesi öneririm. Kodunuzun, kodun ilgili bölümlerinde yer almayan geliştiriciler tarafından incelenmesi gerekir. Gözleri taze olacak ve soruları olacak: bu nedir ve bunu neden yaptın? Ve tabii ki açıklayıcı isimler kullanmanız veya yorum bırakmanız gerekiyor.

12. Kopyala ve yapıştır programlama

Kopyala ve yapıştır programlama, başka birinin kodunun düşüncesizce kopyalanıp yapıştırıldığı ve muhtemelen beklenmedik yan etkilere neden olan bir anti-kalıptır. Örneğin, tam olarak anlamadığımız matematiksel hesaplamalar veya karmaşık algoritmalar içeren kopyalama ve yapıştırma yöntemleri. Bizim özel durumumuz için işe yarayabilir, ancak diğer bazı durumlarda soruna yol açabilir. Bir dizideki maksimum sayıyı belirlemek için bir yönteme ihtiyacım olduğunu varsayalım. İnternette dolaşırken şu çözümü buldum:

public static int max(int[] array) {
   int max = 0;
   for(int i = 0; i < array.length; i++) {
       if (Math.abs(array[i]) > max){
           max = array[i];
       }
   }
   return max;
}
3, 6, 1, 4 ve 2 numaralı bir dizi elde ediyoruz ve yöntem 6 değerini döndürüyor. Harika, öyle kalsın! Ancak daha sonra 2.5, -7, 2 ve 3'ten oluşan bir dizi elde ederiz ve sonucumuz -7 olur. Ve bu sonuç iyi değil. Buradaki sorun, Math.abs()'ın mutlak değeri döndürmesidir. Bunun cehaleti felakete yol açar, ancak yalnızca belirli durumlarda. Çözümü derinlemesine anlamadan, doğrulayamayacağınız birçok durum vardır. Kopyalanan kod, hem biçimsel olarak hem de daha temel bir mimari düzeyde, uygulamanın iç yapısının ötesine geçebilir. Bu tür kodların okunması ve bakımı daha zor olacaktır. Ve tabii ki, başkasının kodunu doğrudan kopyalamanın özel bir tür intihal olduğunu unutmamalıyız.

13. Tekerleği yeniden icat etmek

Tekerleği yeniden icat etmek , bazen kare tekerleği yeniden icat etmek olarak da bilinen bir anti-kalıptır.. Özünde, bu şablon, yukarıda ele alınan kopyala ve yapıştır anti-kalıpının tersidir. Bu anti-kalıpta geliştirici, çözümleri zaten mevcut olan bir sorun için kendi çözümünü uygular. Bazen bu mevcut çözümler, programcının icat ettiğinden daha iyidir. Çoğu zaman, bu yalnızca zaman kaybına ve daha düşük üretkenliğe yol açar: programcı hiç bir çözüm bulamayabilir veya en iyi olmaktan uzak bir çözüm bulabilir. Bununla birlikte, bağımsız bir çözüm oluşturma olasılığını göz ardı edemeyiz çünkü bunu yapmak, programlamayı kopyalayıp yapıştırmaya giden doğrudan bir yoldur. Programcı, hazır çözümler kullanarak veya özel çözümler yaratarak yetkin bir şekilde çözmek için ortaya çıkan belirli programlama görevleri tarafından yönlendirilmelidir. Çok sık, Bu anti-desen kullanmanın nedeni sadece aceledir. Sonuç, hazır çözümlerin yüzeysel bir analizidir (arama). Kare tekerleği yeniden icat etmek, incelenmekte olan anti-desenin olumsuz bir sonuca sahip olduğu bir durumdur. Yani, proje özel bir çözüm gerektiriyor ve geliştirici bunu kötü bir şekilde yaratıyor. Aynı zamanda, iyi bir seçenek zaten var ve diğerleri bunu başarıyla kullanıyor. Alt satır: çok fazla zaman kaybedilir. İlk olarak, çalışmayan bir şey yaratırız. Sonra onu yeniden düzenlemeye çalışırız ve sonunda zaten var olan bir şeyle değiştiririz. Halihazırda çok sayıda uygulama varken kendi özel önbelleğinizi uygulamak buna bir örnektir. Bir programcı olarak ne kadar yetenekli olursanız olun, kare bir tekerleği yeniden icat etmenin en azından zaman kaybı olduğunu unutmamalısınız. Ve bildiğiniz gibi zaman en değerli kaynaktır.

14. Yo-yo sorunu

Yo -yo problemi, uygulamanın yapısının aşırı parçalanma (örneğin, aşırı derecede alt bölümlere ayrılmış bir kalıtım zinciri) nedeniyle aşırı derecede karmaşık olduğu bir anti-kalıptır. "Yo-yo sorunu", kalıtım hiyerarşisi uzun ve karmaşık olan ve derinlemesine iç içe yöntem çağrıları oluşturan bir programı anlamanız gerektiğinde ortaya çıkar. Sonuç olarak, programcıların programın davranışını incelemek için birçok farklı sınıf ve yöntem arasında gezinmesi gerekir. Bu anti-desenin adı oyuncağın adından gelmektedir. Örnek olarak aşağıdaki kalıtım zincirine bakalım: Bir Teknoloji arayüzümüz var:

public interface Technology {
   void turnOn();
}
Aktarım arabirimi onu devralır:

public interface Transport extends Technology {
   boolean fillUp();
}
Ve sonra başka bir arayüzümüz var, GroundTransport:

public interface GroundTransportation extends Transport {
   void startMove();
   void brake();
}
Ve oradan soyut bir Araba sınıfı türetiyoruz:

public abstract class Car implements GroundTransportation {
   @Override
   public boolean fillUp() {
       /* some implementation */
       return true;
   }
   @Override
   public void turnOn() {
       /* some implementation */
   }
   public boolean openTheDoor() {
       /* some implementation */
       return true;
   }
   public abstract void fixCar();
}
Sırada soyut Volkswagen sınıfı var:

public abstract class Volkswagen extends Car {
   @Override
   public void startMove() {
       /* some implementation */
   }
   @Override
   public void brake() {
       /* some implementation */
   }
}
Ve son olarak, belirli bir model:

public class VolkswagenAmarok extends Volkswagen {
   @Override
   public void fixCar(){
       /* some implementation */
   }
}
Bu zincir bizi aşağıdaki gibi soruların cevaplarını aramaya zorluyor:
  1. Kaç tane yöntemi var VolkswagenAmarok?

  2. Maksimum soyutlamayı elde etmek için soru işareti yerine hangi tür girilmelidir:

    
    ? someObj = new VolkswagenAmarok();
           someObj.brake();
    
Bu tür soruları hızlı bir şekilde yanıtlamak zordur - bir göz atmamızı ve araştırmamızı gerektirir ve kafanın karışması kolaydır. Peki ya hiyerarşi çok daha büyük, daha uzun ve daha karmaşıksa, her türlü aşırı yükleme ve geçersiz kılmayla birlikteyse? Sahip olacağımız yapı, aşırı parçalanma nedeniyle gizlenmiş olacaktır. En iyi çözüm, gereksiz bölünmeleri azaltmak olacaktır. Bizim durumumuzda Teknoloji → Araba → VolkswagenAmarok'tan ayrılırdık.

15. Tesadüfi karmaşıklık

Gereksiz karmaşıklık, bir çözüme gereksiz karmaşıklıkların getirildiği bir anti-kalıptır.
"Herhangi bir aptal, bir bilgisayarın anlayabileceği bir kod yazabilir. İyi programcılar, insanların anlayabileceği bir kod yazar." —Martin Fowler
Peki karmaşıklık nedir? Programda her bir işlemin gerçekleştirildiği zorluk derecesi olarak tanımlanabilir. Kural olarak, karmaşıklık iki türe ayrılabilir. Birinci tür karmaşıklık, bir sistemin sahip olduğu fonksiyonların sayısıdır. Sadece bir şekilde azaltılabilir - bazı işlevleri kaldırarak. Mevcut yöntemlerin izlenmesi gerekir. Bir yöntem artık kullanılmıyorsa veya hala kullanılıyorsa ancak herhangi bir değer getirmeden kaldırılmalıdır. Dahası, nerede yatırım yapmaya değer olacağını (çok fazla kod yeniden kullanımı) ve neye hayır diyebileceğinizi anlamak için uygulamadaki tüm yöntemlerin nasıl kullanıldığını değerlendirmeniz gerekir. İkinci tip karmaşıklık, gereksiz karmaşıklıktır. Sadece profesyonel bir yaklaşımla tedavi edilebilir. "Harika" bir şey yapmak yerine (bu hastalığa duyarlı olan sadece genç geliştiriciler değildir), bunu olabildiğince basit bir şekilde nasıl yapacağınızı düşünmeniz gerekir, çünkü en iyi çözüm her zaman basittir. Örneğin, kullanıcı gibi bazı varlıkların açıklamalarını içeren küçük ilişkili tablolarımız olduğunu varsayalım: Anti-paternler nelerdir?  Bazı örneklere bakalım (2. Bölüm) - 3Böylece, kullanıcının kimliğine, açıklamanın yapıldığı dilin kimliğine ve açıklamanın kendisine sahibiz. Benzer şekilde, arabalar, dosyalar, planlar ve müşteri tabloları için yardımcı tanımlayıcılarımız var. O zaman bu tür tablolara yeni değerler eklemek nasıl görünürdü?

public void createDescriptionForElement(ServiceType type, Long languageId, Long serviceId, String description)throws Exception {
   switch (type){
       case CAR:
           jdbcTemplate.update(CREATE_RELATION_WITH_CAR, languageId, serviceId, description);
       case USER:
           jdbcTemplate.update(CREATE_RELATION_WITH_USER, languageId, serviceId, description);
       case FILE:
           jdbcTemplate.update(CREATE_RELATION_WITH_FILE, languageId, serviceId, description);
       case PLAN:
           jdbcTemplate.update(CREATE_RELATION_WITH_PLAN, languageId, serviceId, description);
       case CUSTOMER:
           jdbcTemplate.update(CREATE_RELATION_WITH_CUSTOMER, languageId, serviceId, description);
       default:
           throw new Exception();
   }
}
Ve buna göre, bu numaralandırmaya sahibiz:

public enum ServiceType {
   CAR(),
   USER(),
   FILE(),
   PLAN(),
   CUSTOMER()
}
Her şey basit ve güzel görünüyor... Peki ya diğer yöntemler? Aslında, hepsinin bir sürü switchifadesi ve hemen hemen aynı veritabanı sorguları olacak ve bu da sınıfımızı büyük ölçüde karmaşıklaştıracak ve şişirecektir. Bütün bunlar nasıl daha kolay hale getirilebilir? Enumumuzu biraz yükseltelim:

@Getter
@AllArgsConstructor
public enum ServiceType {
   CAR("cars_descriptions", "car_id"),
   USER("users_descriptions", "user_id"),
   FILE("files_descriptions", "file_id"),
   PLAN("plans_descriptions", "plan_id"),
   CUSTOMER("customers_descriptions", "customer_id");
   private String tableName;
   private String columnName;
}
Artık her tür, tablosunun orijinal alanlarının adlarına sahiptir. Sonuç olarak, açıklama oluşturma yöntemi şöyle olur:

private static final String CREATE_RELATION_WITH_SERVICE = "INSERT INTO %s(language_id, %s, description) VALUES (?, ?, ?)";
public void createDescriptionForElement(ServiceType type, Long languageId, Long serviceId, String description) {
   jdbcTemplate.update(String.format(CREATE_RELATION_WITH_SERVICE, type.getTableName(), type.getColumnName()), languageId, serviceId, description);
   }
Kullanışlı, basit ve kompakt, sence de öyle değil mi? İyi bir geliştiricinin göstergesi kalıpları ne sıklıkta kullandığı değil, anti-kalıplardan ne sıklıkla kaçındığıdır. Cehalet en kötü düşmandır, çünkü düşmanlarınızı görerek tanımanız gerekir. Bugünlük benden bu kadar. Herkese teşekkürler! :)
Yorumlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION