Birçok parametreye sahip tek bir yöntem - sırayla çağırılması gereken birçok yöntem


16

Bazı ham veriler var (kaydırın, döndürün, belirli eksen boyunca ölçekleyin, son konuma döndürün) ve kod okunabilirliğini korumak için bunu yapmanın en iyi yolundan emin değilim. Bir yandan, ihtiyacım olanı yapmak için birçok parametre (10 +) ile tek bir yöntem yapabilirim, ama bu bir kod okuma kabusu. Öte yandan, her biri 1-3 parametre ile birden fazla yöntem yapabilirim, ancak doğru sonuç almak için bu yöntemlerin çok spesifik bir sırayla çağrılması gerekir. Ben bir şey yapmak ve iyi yapmak için yöntemler için en iyi olduğunu okudum, ama sırayla bulmak zor hatalar için kodu açar sırayla olması gereken birçok yöntem olması gibi görünüyor.

Hataları en aza indirecek ve kodun okunmasını kolaylaştıracak bir programlama paradigması var mı?


3
En büyük sorun 'onları sırayla çağırmamak' değil, sizin (veya daha doğrusu gelecekteki bir programcı) bunları sırayla çağırması gerektiğidir. Herhangi bir bakım programlayıcısının ayrıntıları bildiğinden emin olun (Bu, büyük ölçüde gereksinimleri, tasarımı ve teknik özellikleri nasıl belgelediğinize bağlı olacaktır). Birim testlerini, yorumları kullanın ve tüm parametreleri alan ve diğerlerini çağıran yardımcı fonksiyonlar sağlayın
mattnz

Tıpkı elden çıkma gibi, akıcı arayüz ve komut deseni de yararlı olabilir. Bununla birlikte, hangi tasarımın en iyi olduğuna karar vermek size (sahip olarak) ve kütüphanenizin kullanıcılarına (müşterilere) bağlıdır. Diğerlerinin de belirttiği gibi, kullanıcılara, işlemlerin değişmeli olmadığını (yürütme sırasına duyarlı olduklarını) iletmek gerekir, bu olmadan kullanıcılarınız bunları nasıl doğru kullanacaklarını asla öğrenemezler.
rwong

Değişmeli olmayan işlemlere örnekler: görüntü dönüşümleri (döndürme, ölçekleme ve kırpma), matris çarpmaları, vb.
rwong

Belki körelmeyi kullanabilirsiniz: bu yöntemlerin / fonksiyonların yanlış sırada uygulanmasını imkansız hale getirir.
Giorgio

Burada hangi yöntemler üzerinde çalışıyorsunuz? Demek istediğim, standart, onu uygulayan bazı yöntemlere ilettiğiniz bir dönüştürme nesnesini (Java'nın Affine Transform gibi 2D şeyler) geçirmek olduğunu düşünüyorum. Dönüştürmenin içeriği, tasarım üzerindeki ilk işlemleri çağırdığınız sıraya bağlı olarak farklıdır (bu nedenle, "istediğim sırada değil," istediğiniz sırayla çağırırsınız ").
Clockwork-Muse

Yanıtlar:


24

Zamansal bağlantıya dikkat edin . Ancak, bu her zaman bir sorun değildir.

Adımları sırayla gerçekleştirmeniz gerekiyorsa, adım 1, adım 2 için gereken bazı nesneleri üretir (örn. Bir dosya akışı veya başka bir veri yapısı). Bu yalnız İkinci fonksiyon gerektirir gerekir kazara yanlış sırada onları aramak için bile mümkün değildir, ilkinden sonra çağrılabilir.

İşlevselliğinizi ısırık büyüklüğünde parçalara bölerek, her bir parçanın anlaşılması daha kolay ve izole olarak test edilmesi kesinlikle daha kolaydır. 100 satırlık büyük bir fonksiyonunuz varsa ve ortada bir şey varsa, başarısız testiniz size neyin yanlış olduğunu nasıl söyler? Beş satır yönteminizden biri bozulursa, başarısız birim testiniz sizi derhal ilgilenilmesi gereken tek bir kod parçasına yönlendirir.

Bu kompleks kod nasıl olmalıdır bak:

public List<Widget> process(File file) throws IOException {
  try (BufferedReader in = new BufferedReader(new FileReader(file))) {
    List<Widget> widgets = new LinkedList<>();
    String line;
    while ((line = in.readLine()) != null) {
      if (isApplicable(line)) { // Filter blank lines, comments, etc.
        Ore o = preprocess(line);
        Ingot i = smelt(o);
        Alloy a = combine(i, new Nonmetal('C'));
        Widget w = smith(a);
        widgets.add(w);
      }
    }
    return widgets;
  }
}

Ham verileri bitmiş bir widget'a dönüştürme işlemi sırasında herhangi bir noktada, her işlev işlemin bir sonraki adımı için gereken bir şeyi döndürür. İnsan cüruftan bir alaşım oluşturamaz, önce onu koklamak (arındırmak) gerekir. Giriş olarak uygun izin (örn. Çelik) olmadan bir widget oluşturulamaz.

Her bir adımın spesifik detayları test edilebilen bireysel fonksiyonlarda bulunur: madencilik kayalarının tüm sürecini birim test etmek ve widget oluşturmak yerine her bir belirli adımı test edin. Artık "widget oluştur" işleminiz başarısız olursa, belirli bir nedeni daraltabileceğinizden emin olmanın kolay bir yolu var.

Test etme ve doğruluğu kanıtlamanın yararlarının yanı sıra, kodun bu şekilde yazılması çok daha kolaydır. Hiç kimse büyük bir parametre listesini anlayamaz . Küçük parçalara ayırın ve her küçük parçanın ne anlama geldiğini gösterin: bu grokkable .


2
Teşekkürler, bu sorunun üstesinden gelmenin iyi bir yolu olduğunu düşünüyorum. Nesne sayısını artırsa da (ve gereksiz hissedebilir), okunabilirliği korurken düzeni zorlar.
Tomsrobots

10

Tüm kodlarınızın doğru sırada yürütülmesi gerektiğinden, "sırayla yürütülmesi gerekir" bağımsız değişkeni tartışmalıdır. Sonuçta, bir dosyaya yazamaz, sonra açıp kapatamazsınız, değil mi?

Kodunuzu en sürdürülebilir kılan yönlere odaklanmalısınız. Bu genellikle küçük ve kolay anlaşılır yazma işlevleri anlamına gelir. Her fonksiyonun tek bir amacı olmalı ve beklenmeyen yan etkileri olmamalıdır.


5

Bir » ImageProcesssor « (ya da projenize uygun herhangi bir ad) ve gerekli tüm parametreleri tutan ProcessConfiguration yapılandırma nesnesini yaratacağım .

 ImageProcessor p = new ImageProcessor();

 ProcessConfiguration config = new processConfiguration().setTranslateX(100)
                                                         .setTranslateY(100)
                                                         .setRotationAngle(45);
 p.process(image, config);

Görüntü işlemcisinin içinde, tüm süreci bir mehtodun arkasına kapsüllersiniz process()

public class ImageProcessor {

    public Image process(Image i, ProcessConfiguration c){
        Image processedImage=i.getCopy();
        shift(processedImage, c);
        rotate(processedImage, c);
        return processedImage;
    }

    private void rotate(Image i, ProcessConfiguration c) {
        //rotate
    }

    private void shift(Image i, ProcessConfiguration c) {
        //shift
    }
}

Bu yöntem, doğru sırayla dönüşüm yöntemlerini çağırır shift(), rotate(). Her yöntem, geçirilen ProcessConfiguration öğesinden uygun parametreleri alır .

public class ProcessConfiguration {

    private int translateX;

    private int rotationAngle;

    public int getRotationAngle() {
        return rotationAngle;
    }

    public ProcessConfiguration setRotationAngle(int rotationAngle){
        this.rotationAngle=rotationAngle;
        return this;
    }

    public int getTranslateY() {
        return translateY;
    }

    public ProcessConfiguration setTranslateY(int translateY) {
        this.translateY = translateY;
        return this;
    }

    public int getTranslateX() {
        return translateX;
    }

    public ProcessConfiguration setTranslateX(int translateX) {
        this.translateX = translateX;
        return this;
    }

    private int translateY;

}

Sıvı arayüzleri kullandım

public ProcessConfiguration setRotationAngle(int rotationAngle){
    this.rotationAngle=rotationAngle;
    return this;
}

bu da şık bir başlatmaya izin verir (yukarıda görüldüğü gibi).

Bariz avantaj, bir parametrede gerekli parametreleri kapsüllemek. Yöntem imzalarınız okunabilir hale gelir:

private void shift(Image i, ProcessConfiguration c)

Yaklaşık olduğu kayması bir görüntü ve ayrıntılı parametreler nasılsa edilir yapılandırılmış .

Alternatif olarak, bir ProcessingPipeline oluşturabilirsiniz :

public class ProcessingPipeLine {

    Image i;

    public ProcessingPipeLine(Image i){
        this.i=i;
    };

    public ProcessingPipeLine shift(Coordinates c){
        shiftImage(c);
        return this;
    }

    public ProcessingPipeLine rotate(int a){
        rotateImage(a);
        return this;
    }

    public Image getResultingImage(){
        return i;
    }

    private void rotateImage(int angle) {
        //shift
    }

    private void shiftImage(Coordinates c) {
        //shift
    }

}

Bir yönteme yapılan bir çağrı, processImageböyle bir boru hattını başlatır ve neyin hangi sırayla yapıldığını şeffaf hale getirir: kaydırma , döndürme

public Image processImage(Image i, ProcessConfiguration c){
    Image processedImage=i.getCopy();
    processedImage=new ProcessingPipeLine(processedImage)
            .shift(c.getCoordinates())
            .rotate(c.getRotationAngle())
            .getResultingImage();
    return processedImage;
}

3

Bir çeşit köri kullanmayı düşündün mü ? Bir sınıfınız Processeeve bir sınıfınız olduğunu düşünün Processor:

class Processor
{
    private final Processee _processee;

    public Processor(Processee p)
    {
        _processee = p;
    }

    public void process(T1 a1, T2 a2)
    {
        // Process using a1
        // then process using a2
    }
}

Şimdi sınıf yerine Processoriki sınıf tarafından Processor1ve Processor2:

class Processor1
{
    private final Processee _processee;

    public Processor1(Processee p)
    {
        _processee = p;
    }

    public Processor2 process(T1 a1)
    {
        // Process using argument a1

        return new Processor2(_processee);
    }
}

class Processor2
{
    private final Processee _processee;

    public Processor(Processee p)
    {
        _processee = p;
    }

    public void process(T2 a2)
    {
        // Process using argument a2
    }
}

Daha sonra aşağıdaki işlemleri kullanarak işlemleri doğru sırayla çağırabilirsiniz:

new Processor1(processee).process(a1).process(a2);

İkiden fazla parametreniz varsa bu deseni birden çok kez uygulayabilirsiniz. Ayrıca, bağımsız değişkenleri istediğiniz gibi gruplayabilirsiniz, yani her processyöntemin tam olarak bir bağımsız değişken almasını sağlamanız gerekmez .


Neredeyse aynı fikrimiz vardı;) Tek fark, Pipeline'ınızın katı bir işleme siparişi uygulamasıdır.
Thomas Junk

@ThomasJunk: Anladığım kadarıyla, bu bir gerekliliktir: "doğru sonuçları elde etmek için bu yöntemlerin çok özel bir sırayla çağrılması gerekir". Sıkı bir yürütme düzenine sahip olmak, işlev kompozisyonuna çok benziyor.
Giorgio

Ve ben de öyle. Ancak, sipariş değişikliği işlenirse, çok sayıda yeniden düzenleme yapmak zorundasınız;)
Thomas Junk

@ThomasJunk: Doğru. Gerçekten uygulamaya bağlıdır. İşleme adımları çok sık değiştirilebilirse, muhtemelen yaklaşımınız daha iyidir.
Giorgio
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.