Tek yöntemle sınıf - en iyi yaklaşım?


172

Diyelim ki tek bir işlevi yerine getirmek üzere bir sınıfım var İşlevi gerçekleştirdikten sonra yok edilebilir. Bu yaklaşımlardan birini tercih etmek için herhangi bir neden var mı?

// Initialize arguments in constructor
MyClass myObject = new MyClass(arg1, arg2, arg3);
myObject.myMethod();

// Pass arguments to method
MyClass myObject = new MyClass();
myObject.myMethod(arg1, arg2, arg3);

// Pass arguments to static method
MyClass.myMethod(arg1, arg2, arg3);

Farklı durumlar için yönergeler almaya çalışmak için kasıtlı olarak ayrıntılar hakkında belirsiz oluyordum. Ancak Math.random () gibi basit kütüphane işlevlerini gerçekten aklıma getirmedim. Bazı belirli, karmaşık bir görevi yerine getiren, ancak bunu yapmak için yalnızca bir (genel) yöntem gerektiren sınıflar daha düşünüyorum.

Yanıtlar:


264

Statik yöntemlerle doldurulmuş faydalı sınıfları severdim. Aksi halde fazlalık ve bakım cehennemine neden olacak yardımcı yöntemlerin büyük bir konsolidasyonunu yaptılar. Kullanımı çok kolay, örnekleme yok, elden çıkarma yok, sadece unutun. Sanırım bu hizmet odaklı bir mimari yaratmaya yönelik ilk isteksiz girişimim - sadece işlerini yapan ve başka hiçbir şey yapmayan vatansız hizmetler. Ancak bir sistem büyüdükçe, ejderhalar da geliyor.

Çok Biçimlilik
Demek ki mutlu bir şekilde vızıldayan UtilityClass.SomeMethod yöntemine sahibiz. Aniden işlevselliği biraz değiştirmemiz gerekiyor. İşlevlerin çoğu aynıdır, ancak yine de birkaç parçayı değiştirmek zorundayız. Statik bir yöntem olmasaydı, bir türev sınıf oluşturabilir ve yöntem içeriğini gerektiği gibi değiştirebiliriz. Statik bir yöntem olduğu için yapamayız. Elbette, eski yöntemden önce veya sonra işlevsellik eklememiz gerekirse, yeni bir sınıf oluşturabilir ve içindeki eski sınıfı çağırabiliriz - ama bu sadece iğrenç.

Arayüz woes
Statik yöntemler, mantık nedenleriyle arayüzler üzerinden tanımlanamaz. Statik yöntemleri geçersiz kılamayacağımız için, statik sınıflar, arayüzleri tarafından çevrilmemiz gerektiğinde işe yaramaz. Bu, statik sınıfları bir strateji modelinin parçası olarak kullanmamızı sağlar. Arabirim yerine delegeleri geçirerek bazı sorunları çözebiliriz .

Test Etme
Bu, temel olarak yukarıda belirtilen arayüz sıkıntılarıyla el ele gider. Uygulamaları değiştirme yeteneğimiz çok sınırlı olduğundan, üretim kodunu test koduyla değiştirme konusunda da sorun yaşayacağız. Yine, bunları tamamlayabiliriz, ancak gerçek nesneler yerine sarmalayıcıları kabul edebilmek için kodumuzun büyük bölümlerini değiştirmemizi gerektirecektir.

Fosters blobs
Statik yöntemler genellikle faydalı yöntemler olarak kullanıldığından ve yardımcı yöntemler genellikle farklı amaçlara sahip olacağından, tutarlı olmayan işlevsellikle doldurulmuş büyük bir sınıfla hızla sonuçlanırız - ideal olarak, her sınıfın sistem içinde tek bir amacı olmalıdır . Amaçları iyi tanımlandığı sürece derslerin beş katını tercih ederim.

Parametre sürünmesi
Başlangıç ​​olarak, bu küçük şirin ve masum statik yöntem tek bir parametre alabilir. İşlevler büyüdükçe birkaç yeni parametre eklenir. Yakında isteğe bağlı başka parametreler eklenir, bu nedenle yöntemin aşırı yüklenmelerini oluştururuz (veya yalnızca bunları destekleyen dillerde varsayılan değerler ekleriz). Çok geçmeden, 10 parametre alan bir yöntemimiz var. Sadece ilk üçü gerçekten gereklidir, parametre 4-7 isteğe bağlıdır. Ancak parametre 6 belirtilirse, 7-9'un da doldurulması gerekir ... Bu statik yöntemin ne yaptığını yapmak için tek bir sınıf yaratmış olsaydık, bunu gerekli parametreleri alarak çözebiliriz. Kullanıcının özellikler yoluyla isteğe bağlı değerleri ayarlamasına veya aynı anda birden fazla birbirine bağlı değer belirleme yöntemlerine izin vermesine olanak tanır. Ayrıca, bir yöntem bu karmaşıklığa ulaştıysa,

Tüketicilere sebepsiz yere sınıf örneği yaratmalarını isteme
En yaygın argümanlardan biri, neden sınıfımızın tüketicilerinin bu tek yöntemi çağırmak için bir örnek oluşturduğunu, daha sonra örnek için bir faydası olmadığı talebidir? Bir sınıf örneği oluşturmak çoğu dilde çok ucuz bir işlemdir, bu nedenle hız bir sorun değildir. Tüketiciye ekstra bir kod satırı eklemek, gelecekte çok daha sürdürülebilir bir çözümün temelini atmak için düşük bir maliyettir. Son olarak, örnek oluşturmaktan kaçınmak istiyorsanız, sınıfınızın vatansız olması şartı olmasına rağmen, kolayca yeniden kullanılmasına izin veren tek bir sınıf sarmalayıcı oluşturun. Vatansız değilse, yine de uzun vadede size tüm avantajları sunarken, her şeyi idare eden statik sarma yöntemleri oluşturabilirsiniz. En sonunda,

Mutlaklar Sadece bir Sith fırsatlar
Tabii ki, statik yöntemlerin sevmeyişimden istisnaları vardır. Şişkinlik için herhangi bir risk oluşturmayan gerçek yarar sınıfları statik yöntemler için mükemmel durumlardır - System.Convert örnek olarak. Projeniz gelecekteki bakım için herhangi bir gereksinimi olmayan bir kerelik ise, genel mimari gerçekten çok önemli değil - statik veya statik olmayan, gerçekten önemli değil - geliştirme hızı önemlidir.

Standartlar, standartlar, standartlar!
Örnek yöntemlerini kullanmak, statik yöntemleri de kullanmanızı engellemez veya tersi de geçerlidir. Farklılaşmanın arkasında akıl yürütme olduğu ve standartlaştırıldığı sürece. Farklı uygulama yöntemleri ile yayılan bir iş katmanına bakmaktan daha kötü bir şey yoktur.


Mark'ın dediği gibi, polimorfizm, yöntemleri 'strateji' parametreleri olarak iletebilmek ve seçenekleri yapılandırabilmek / ayarlayabilmek bir örneği kullanmanın nedenleridir . Örneğin: Bir ListDelimiter veya DelimiterParser kullanımına sınırlayıcılar ne olduğuna yapılandırılabilir / kabul çözümlenen jeton gelen Döşeme boşluk, edip edip dirseğe nasıl boş / boş listesini tedavi etmek .. vs listesi
Thomas W

4
"Bir sistem büyüdükçe ..."
Rodney Gitzel

3
@ user3667089 Sınıfın mantıksal olarak var olması için gereken genel argümanlar, yapıcıya ileteceğim. Bunlar tipik olarak çoğu / tüm yöntemlerde kullanılır. Yönteme özgü argümanlar Bu yönteme geçirdim.
Mark S. Rasmussen

1
Gerçekten statik bir sınıfla yaptığınız şey, ham, nesne yönelimli olmayan C'ye (bellek tarafından yönetilen de olsa) geri dönüyor - tüm işlevler küresel bir alanda, hayır this, küresel durumu yönetmeniz gerekiyor vb. prosedürel programlamayı seviyorsanız bunu yapın. Ancak, OO'nun yapısal faydalarının çoğunu kaybettiğinizi takdir edin.
Mühendis

1
Bu nedenlerin çoğunun statik yöntemlerle değil, kötü kod yönetimiyle ilgisi vardır. F # ve diğer işlevsel dillerde temel olarak statik yöntemleri (örnek durumu olmayan fonksiyonlar) her yerde kullanırız ve bu problemleri yaşamazız. Bununla birlikte, F #, işlevleri kullanmak için daha iyi bir paradigma sağlar (işlevler birinci sınıftır, işlevler curried ve kısmen uygulanabilir), bu da onları C # 'dan daha uygulanabilir kılar. Sınıfları kullanmanın daha büyük bir nedeni, C # için bunun için oluşturulmuş olmasıdır. .NET Core'daki tüm Bağımlılık Enjeksiyon yapıları, deps'li örnek sınıfları etrafında döner.
Justin J Stark

89

Statik yolu tercih ederim. Sınıf bir nesneyi temsil etmediği için nesnenin bir örneğini oluşturmak mantıklı değildir.

Yalnızca yöntemleri için var olan sınıflar statik bırakılmalıdır.


19
-1 "Yalnızca yöntemleri için var olan sınıflar statik bırakılmalıdır."
Rookian

8
@Rookian neden buna katılmıyorsun?
jjnguy

6
bir örnek depo deseni olabilir. Sınıf yalnızca yöntemler içerir. Yaklaşımınızı takip etmek arayüzlerin kullanılmasına izin vermez. => kuplajı
artır

1
@Rook, genel olarak insanlar sadece yöntemler için kullanılan sınıflar oluşturmamalıdır. Örnekte verdiğiniz statik yöntemler iyi bir fikir değildir. Ancak basit yarar yöntemleri için statik yol en iyisidir.
jjnguy

9
Kesinlikle doğru :) Senin koşul ile "basit yarar yöntemleri için" tamamen sana katılıyorum, ama cevabında yanlış imo olan genel bir kural yaptın.
Rookian

18

İşlevi yürütmek için sınıfın bir örneğinin oluşturulması için bir neden yoksa, statik uygulamayı kullanın. Neden bu sınıftaki tüketiciler, ihtiyaç duyulmadığında bir örnek oluştursun?


16

Nesnenin durumunu kaydetmeniz gerekmiyorsa, ilk etapta onu başlatmaya gerek yoktur. Parametreleri ilettiğiniz tek statik yöntemle giderdim.

Ayrıca düzinelerce alakasız statik yöntemi olan dev bir Utils sınıfına karşı da uyarırdım. Bu aceleyle dağınık ve hantal olabilir. Her biri az sayıda, ilgili yöntemle birçok sınıfa sahip olmak daha iyidir.


6

Statik Yöntem formatının daha iyi bir seçenek olacağını söyleyebilirim. Sınıfı da statik hale getirirdim, bu şekilde sınıfın bir örneğini yanlışlıkla yaratma konusunda endişelenmenize gerek kalmazdı.


5

Durumun burada olduğunu gerçekten bilmiyorum, ama arg1, arg2 veya arg3'ün ait olduğu sınıflardan birine bir yöntem olarak koymaya bakardım - Semantik olarak bu sınıflardan birinin yöntem.


4

Sağlanan bilgilere dayanarak cevaplamanın zor olduğunu öneririm.

Benim içim sadece bir yönteme sahip olacaksanız ve derhal sınıfı fırlatacaksanız, o zaman tüm parametreleri alan statik bir sınıf haline getirmenizdir.

Tabii ki, bu yöntem için neden tek bir sınıf oluşturmanız gerektiğini tam olarak söylemek zor. Çoğu varsayıldığı gibi tipik "Utilities sınıfı" durumu mu? Yoksa gelecekte daha fazla olabilecek bir çeşit kural sınıfı mı uyguluyorsunuz?

Örneğin, bu sınıfın takılabilir olmasını sağlayın. Daha sonra bir yönteminiz için bir Arayüz oluşturmak istersiniz ve ardından tüm parametrelerin yapıcıya değil, arayüze geçirilmesini istersiniz, ancak statik olmasını istemezsiniz.


3

Sınıfınız statik hale getirilebilir mi?

Eğer öyleyse, o zaman tüm tek fonksiyonlu sınıflarımı koyacağım bir 'Utilities' sınıfı yapardım.


2
Bir şekilde katılmıyorum. Katıldığım projelerde, Utilities sınıfınızda yüzlerce ilişkisiz yöntem olabilir.
Paul Tomblin

3
@ Paul: Genel olarak kabul edildi. Tamamen ilgisiz düzinelerce (veya evet, yüzlerce) dersi görmekten nefret ediyorum. Bu yaklaşımı benimsemek gerekirse, en azından bunları daha küçük, ilgili yardımcı programlara (EG, FooUtilities, BarUtilities, vb.) Bölün.
John Rudy

9
bir sınıf bir sorumluluk.
Andreas Petersson

3

Bu yöntem vatansızsa ve etrafından geçirmeniz gerekmiyorsa, statik olarak tanımlamak en mantıklıdır. Yöntemi aktarmanız gerekiyorsa, önerilen diğer yaklaşımlarınızdan biri yerine bir temsilci kullanmayı düşünebilirsiniz .


3

Basit uygulamalar ve internalyardımcılar için statik bir yöntem kullanırdım. Bileşenleri olan uygulamalar için, Yönetilebilir Genişletilebilirlik Çerçevesi'ni seviyorum . İşte API'larımda bulacağınız kalıpları tanımlamak için yazdığım bir belgeden bir alıntı.

  • Hizmetler
    • Bir I[ServiceName]Servicearayüz tarafından tanımlanır .
    • Arabirim türüne göre dışa ve içe aktarma.
    • Tek uygulama, ana bilgisayar uygulaması tarafından sağlanır ve dahili ve / veya uzantılar tarafından tüketilir.
    • Hizmet arabirimindeki yöntemler iş parçacığı için güvenlidir.

Bir örnek olarak:

public interface ISettingsService
{
    string ReadSetting(string name);

    void WriteSetting(string name, string value);
}

[Export]
public class ObjectRequiringSettings
{
    [Import]
    private ISettingsService SettingsService
    {
        get;
        set;
    }

    private void Foo()
    {
        if (SettingsService.ReadSetting("PerformFooAction") == bool.TrueString)
        {
            // whatever
        }
    }
}

2

Yapıcıda her şeyi yaparım. şöyle:

new MyClass(arg1, arg2, arg3);// the constructor does everything.

veya

MyClass my_object(arg1, arg2, arg3);

MyClass türünde bir nesnenin yanı sıra herhangi bir şey döndürmeniz gerekirse ne olur?
Kertenkele Bill

13
Bir kurucuya yürütme mantığını yerleştirmenin kötü bir uygulama olduğunu düşünüyorum. İyi gelişme anlambilimle ilgilidir. Anlamsal olarak, bir yapıcı diğer görevleri yerine getirmek için değil, bir nesne inşa etmek için vardır.
mstrobl

bill, dönüş değerine ihtiyacınız varsa, başvuruyu ret değerine iletin: MyClass myObject (arg1, arg2, arg3, retvalue); mstrobl, nesneye ihtiyaç duyulmuyorsa, neden yaratılsın? ve bu hile aslında bazı durumlarda size yardımcı olabilir.

Bir amacı, herhangi bir durum varsa, örneğin, bir o zaman olacaktır örneklilik, değişkenler değil de yığın ya yığında - bir ayırma üretir. C ++ 'daki nesneleri, sadece bir yapıya işaret eden gizli bir ilk parametre ile C fonksiyonu çağrıları olarak düşünebilirsiniz.
mstrobl

mstrobl, bu steroid üzerinde ac fonksiyonu gibi çünkü ctor kullanabilirsiniz özel fonksiyonları tanımlayabilirsiniz. verileri özel işlevlere geçirmeye yardımcı olması için sınıf üyesi değişkenlerini de kullanabilirsiniz.

0

Dikkate alınması gereken bir diğer önemli husus, sistemin çok iş parçacıklı bir ortamda çalışıp çalışmayacağı ve statik bir yöntem veya değişkenlere sahip olmanın iş parçacığı açısından güvenli olup olmayacağıdır ...

Sistem durumuna dikkat etmelisiniz.


0

Bu durumu hep birlikte önleyebilirsiniz. Almak için refactor deneyin arg1.myMethod1(arg2, arg3). Daha mantıklıysa arg1'i arg2 veya arg3 ile değiştirin.

Arg1 sınıfı üzerinde denetiminiz yoksa, onu dekore edin:

class Arg1Decorator
    private final T1 arg1;
    public Arg1Decorator(T1 arg1) {
        this.arg1 = arg1;
    }
    public T myMethod(T2 arg2, T3 arg3) {
        ...
    }
 }

 arg1d = new Arg1Decorator(arg1)
 arg1d.myMethod(arg2, arg3)

Bunun nedeni, OOP'ta bu verileri işleyen veri ve yöntemlerin birbirine ait olmasıdır. Ayrıca Mark'ın bahsettiği tüm avantajları elde edersiniz.


0

Sınıfınızın özellikleri veya sınıf örneği yapıcılarda veya yöntemlerinizde kullanılmayacaksa, yöntemlerin 'statik' desen gibi tasarlanması önerilmez. statik yöntem her zaman 'yardım' şeklinde düşünülmelidir.


0

Sadece bir şey yapmak ya da bir şey yapmak ve iade etmek isteyip istemediğinize bağlı olarak bunu yapabilirsiniz:

public abstract class DoSomethingClass<T>
{
    protected abstract void doSomething(T arg1, T arg2, T arg3);
}

public abstract class ReturnSomethingClass<T, V>
{
    public T value;
    protected abstract void returnSomething(V arg1, V arg2, V arg3);
}

public class DoSomethingInt extends DoSomethingClass<Integer>
{
    public DoSomethingInt(int arg1, int arg2, int arg3)
    {
        doSomething(arg1, arg2, arg3);
    }

    @Override
    protected void doSomething(Integer arg1, Integer arg2, Integer arg3)
    {
        // ...
    }
}

public class ReturnSomethingString extends ReturnSomethingClass<String, Integer>
{
    public ReturnSomethingString(int arg1, int arg2, int arg3)
    {
        returnSomething(arg1, arg2, arg3);
    }

    @Override
    protected void returnSomething(Integer arg1, Integer arg2, Integer arg3)
    {
        String retValue;
        // ...
        value = retValue;
    }
}

public class MainClass
{
    static void main(String[] args)
    {
        int a = 3, b = 4, c = 5;

        Object dummy = new DoSomethingInt(a,b,c);  // doSomething was called, dummy is still around though
        String myReturn = (new ReturnSomethingString(a,b,c)).value; // returnSomething was called and immediately destroyed
    }
}
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.