Kapsülleme aşırı kullanımından muzdarip miyim?


11

Çeşitli projelerde kodumda bana kod kokusu ve yapmak kötü bir şey gibi görünen bir şey fark ettim, ancak bununla başa çıkamıyorum.

"Temiz kod" yazmaya çalışırken, kodumu okumayı kolaylaştırmak için özel yöntemleri aşırı kullanma eğilimindeyim. Sorun kod gerçekten daha temiz ama aynı zamanda test etmek daha zor (evet biliyorum özel yöntemleri test edebilirsiniz ...) ve genel olarak benim için kötü bir alışkanlık gibi görünüyor.

Aşağıda, bir .csv dosyasından bazı verileri okuyan ve bir grup müşteri (çeşitli alanlara ve özniteliklere sahip başka bir nesne) döndüren bir sınıf örneği verilmiştir.

public class GroupOfCustomersImporter {
    //... Call fields ....
    public GroupOfCustomersImporter(String filePath) {
        this.filePath = filePath;
        customers = new HashSet<Customer>();
        createCSVReader();
        read();
        constructTTRP_Instance();
    }

    private void createCSVReader() {
        //....
    }

    private void read() {
        //.... Reades the file and initializes the class attributes
    }

    private void readFirstLine(String[] inputLine) {
        //.... Method used by the read() method
    }

    private void readSecondLine(String[] inputLine) {
        //.... Method used by the read() method
    }

    private void readCustomerLine(String[] inputLine) { 
        //.... Method used by the read() method
    }

    private void constructGroupOfCustomers() {
        //this.groupOfCustomers = new GroupOfCustomers(**attributes of the class**);
    }

    public GroupOfCustomers getConstructedGroupOfCustomers() {
        return this.GroupOfCustomers;
    }
}

Sınıfın sadece işi yapmak için bazı özel yöntemleri çağıran bir kurucuya sahip olduğunu görebildiğiniz gibi, bunun genel olarak iyi bir uygulama olmadığını biliyorum, ancak bu durumda yöntemleri herkese açık hale getirmek yerine sınıftaki tüm işlevleri kapsüllemeyi tercih ediyorum bir müşteri şu şekilde çalışmalıdır:

GroupOfCustomersImporter importer = new GroupOfCustomersImporter(filepath)
importer.createCSVReader();
read();
GroupOfCustomer group = constructGoupOfCustomerInstance();

İstemcinin yan kodunda istemci sınıfını uygulama ayrıntıları ile rahatsız eden işe yaramaz kod satırları koymak istemediğim için bunu tercih ederim.

Peki, bu aslında kötü bir alışkanlık mı? Evet ise, nasıl önleyebilirim? Yukarıdakilerin sadece basit bir örnek olduğunu lütfen unutmayın. Aynı durumun biraz daha karmaşık bir şeyde olduğunu hayal edin.

Yanıtlar:


17

Uygulama ayrıntılarını istemciden gizlemek istediğiniz kadar doğru yolda olduğunuzu düşünüyorum. Sınıfınızı, istemcinin gördüğü arayüz, karşılaşabileceğiniz en basit ve en özlü API olacak şekilde tasarlamak istiyorsunuz. İstemci uygulama ayrıntılarıyla "rahatsız edilmez", aynı zamanda kodun arayanlarının değiştirilmesi gerektiğinden endişe etmeden temel kodu yeniden düzenleme konusunda da özgür olursunuz. Farklı modüller arasındaki bağlantıyı azaltmanın gerçek bir yararı vardır ve bunun için kesinlikle çaba göstermelisiniz.

Bu nedenle, az önce verdiğim tavsiyelere uyursanız, kodunuzda zaten fark ettiğiniz bir şeyle karşılaşacaksınız, genel bir arayüzün arkasında gizli kalan ve kolayca erişilemeyen bir sürü mantık.

İdeal olarak, sınıfınızı yalnızca genel arayüzüne göre birim test edebilmelisiniz ve harici bağımlılıkları varsa, test edilen kodu izole etmek için sahte / sahte / saplama nesneleri eklemeniz gerekebilir.

Ancak, bunu yaparsanız ve yine de sınıfın her bölümünü kolayca test edemeyeceğinizi düşünüyorsanız, bir sınıfınızın çok fazla şey yapması muhtemeldir. SRP ilkesinin ruhuyla, Michael Feathers'ın "Sprout Class Pattern" adını verdiği şeyi takip edebilir ve orijinal sınıfınızın bir parçasını yeni bir sınıfa çıkarabilirsiniz.

Örneğinizde, bir metin dosyasını okumaktan da sorumlu olan bir ithalatçı sınıfınız var. Seçeneklerinizden biri, ayrı bir InputFileParser sınıfına bir yığın dosya okuma kodu ayıklamaktır. Artık tüm bu özel işlevler halka açık ve bu nedenle kolayca test edilebilir hale geliyor. Aynı zamanda ayrıştırıcı sınıfı, harici istemciler tarafından görülebilir bir şey değildir ("dahili" olarak işaretleyin, başlık dosyalarını yayınlamayın veya yalnızca API'nızın bir parçası olarak reklamını yapmayın) orijinali kullanmaya devam edecektir. arayüzü kısa ve tatlı olmaya devam edecek ithalatçı.


1

Sınıf yapıcısına çok fazla yöntem çağrısı koymanın alternatifi olarak - genellikle kaçınmak için bir alışkanlık olduğunu kabul ediyorum - bunun yerine müşteriyi rahatsız etmek istemediğiniz tüm ekstra başlatma öğelerini işlemek için bir fabrika yöntemi oluşturabilirsiniz. tarafı ile. Bu, constructGroupOfCustomers()yönteminiz gibi bir şeye genel olarak bakmak için bir yöntem ortaya koyacağınız ve bunu sınıfınızın statik bir yöntemi olarak kullandığınız anlamına gelir:

GroupOfCustomersImporter importer = 
    new GroupOfCustomersImporter.CreateInstance();

veya ayrı bir fabrika sınıfının yöntemi olarak belki de:

ImporterFactory factory = new ImporterFactory();
GroupOfCustomersImporter importer = factory.CreateGroupOfCustomersImporter();

Bunlar kafamın tepesinden düştüğü düşünülen ilk seçenek.

Ayrıca, içgüdülerinizin size bir şeyler anlatmaya çalıştığını düşünmeye değer. Bağırsaklar size kodun koklamaya başladığını söylediğinde, muhtemelen kokuyor ve dizginlenmeden önce bu konuda bir şeyler yapmak daha iyi! Tabii ki, kodunuzda kendiliğinden yanlış bir şey olmayabilir, ancak hızlı bir yeniden kontrol ve bir refactor, konuyla ilgili düşüncelerinizi çözmenize yardımcı olacak, böylece bir bina oluşturursanız endişelenmeden başka şeylere güvenle geçebilirsiniz potansiyel kart evi. Bu özel durumda, inşaatçının bir fabrikaya karşı veya çağrılacak sorumluluklarının bir kısmını devretmek, sınıfınızın ve potansiyel gelecekteki torunlarının örneklenmesi üzerinde daha fazla kontrol sahibi olmanızı sağlar.


1

Kendi cevabımı eklemek, çünkü bir yorum için çok uzun sürdü.

Kapsülleme ve testin tam tersi olduğu konusunda kesinlikle haklısınız - neredeyse her şeyi gizlerseniz test etmek zor.

Bununla birlikte, dosya adı yerine bir akış vererek örneği daha test edilebilir hale getirebilirsiniz - bu şekilde bellekten bilinen bir csv veya isterseniz bir dosya sağlarsınız. Ayrıca, http desteği eklemeniz gerektiğinde sınıfınızı daha sağlam hale getirecektir;)

Ayrıca, tembel init kullanmaya bakın:

public class Csv
{
  private CustomerList list;

  public CustomerList get()
  {
    if(list == null)
        load();
     return list;
  }
}

1

Yapıcı, bazı değişkenleri veya nesnenin durumunu başlatmak dışında çok fazla şey yapmamalıdır. Yapıcıda çok fazla şey yapmak, çalışma zamanı istisnalarını artırabilir. İstemci böyle bir şey yapıyor kaçınarak denemek istiyorsunuz:

MyClass a;
try {
   a = new MyClass();
} catch (MyException e) {
   //do something
}

Yapmak yerine:

MyClass a = new MyClass(); // Or a factory method
try {
   a.doSomething();
} catch (MyException e) {
   //do something
}
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.