İş hatalarına karşı statik tip denetimi kullanma


13

Statik tip kontrol hayranıyım. Bunun gibi aptalca hatalar yapmanı engeller:

// java code
Adult a = new Adult();
a.setAge("Roger"); //static type checker would complain
a.setName(42); //and here too

Ancak bu gibi aptalca hatalar yapmanıza engel olmaz:

Adult a = new Adult();
// obviously you've mixed up these fields, but type checker won't complain
a.setAge(150); // nobody's ever lived this old
a.setWeight(42); // a 42lb adult would have serious health issues

Sorun, açıkça farklı türde bilgileri temsil etmek için aynı türü kullandığınızda ortaya çıkar. Bunun için iyi bir çözüm Integer, sadece iş mantığı hatalarını önlemek, ancak işlevsellik eklemek için , sınıfı genişletmek olacağını düşünüyordum . Örneğin:

class Age extends Integer{};
class Pounds extends Integer{};

class Adult{
    ...
    public void setAge(Age age){..}
    public void setWeight(Pounds pounds){...}
}

Adult a = new Adult();
a.setAge(new Age(42));
a.setWeight(new Pounds(150));

Bu iyi bir uygulama mıdır? Yoksa böyle kısıtlayıcı bir tasarımla yolda beklenmedik mühendislik problemleri var mı?


5
Olmaz a.SetAge( new Age(150) )hala derlemek?
John Wu

1
Java int türünün sabit bir aralığı vardır. Açıkçası, özel aralıklarla tamsayılara sahip olmak istersiniz, örn. Tamsayı <18, 110>. Bazı (ana akım olmayan) dillerin sunduğu ayrıntılandırma türlerini veya bağımlı türleri arıyorsunuz.
Theodoros Chatzigiannakis

3
Bahsettiğiniz şey tasarım yapmanın harika bir yolu! Ne yazık ki, Java o kadar ilkel tip sisteme sahip, doğru yapmak zor. Ve bunu yapmaya çalışsanız bile, düşük performans veya düşük geliştirici verimliliği gibi yan etkilere neden olabilir.
Euphoric

@JohnWu evet örneğiniz hala derlenecek, ancak bu mantık için tek hata noktası bu. new Age(...)Nesnenizi bildirdikten sonra , Weightbaşka bir yerde yanlış bir tür değişkenine yanlış atayamazsınız . Hataların meydana gelebileceği yer sayısını azaltır.
J-bob

Yanıtlar:


12

Temelde bir birim sistemi istiyorsunuz (birim testleri değil, "fiziksel birim" deki gibi "birim", metre, volt vb. Gibi).

Kodunuzda Agezamanı ve Poundskütleyi temsil eder. Bu birim dönüştürme, temel birimler, hassasiyet vb. Şeylere yol açar.


Java'ya böyle bir şey girme girişimleri vardı, örneğin:

Daha sonraki ikisi bu github şeyin içinde yaşıyor gibi görünüyor: https://github.com/unitsofmeasurement


C ++, Boost üzerinden birimlere sahiptir


LabView bir grup ünite ile birlikte gelir .


Başka dillerde başka örnekler de var. (düzenlemeler kabul edilir)


Bu iyi bir uygulama mıdır?

Yukarıda görebileceğiniz gibi, bir birimin değerleri birimlerle işlemesi olasılığı ne kadar yüksek olursa birimleri o kadar doğal olarak destekler. LabView genellikle ölçüm cihazlarıyla etkileşime girmek için kullanılır. Bu nedenle, dilde böyle bir özelliğe sahip olmak ve onu kullanmak kesinlikle iyi bir uygulama olarak kabul edilir.

Ancak, bu tür bir titizlik için talebin düşük olduğu herhangi bir genel amaçlı üst düzey dilde, muhtemelen beklenmediktir.

Yoksa böyle kısıtlayıcı bir tasarımla yolda beklenmedik mühendislik problemleri var mı?

Benim tahminim olacaktır: performans / bellek. Eğer değerler bir sürü başa Eğer değeri başına bir nesnenin havai olabilir düşünmez. Ancak her zaman olduğu gibi: Erken optimizasyon tüm kötülüklerin köküdür .

Bence daha büyük "sorun" birim alışılagelmiş olarak tanımlanır, çünkü birim genellikle böyle tanımlanır:

class Adult
{
    ...
    public void setAge(int ageInYears){..}

İnsanlar, bir nesneyi intbirim sistemlere aşina olmadıkları zaman, basit bir şekilde tanımlanabilecek bir şey için bir değer olarak geçirmek zorunda kaldıklarında kafanız karışacaktır .


"İnsanlar bir nesneyi basit bir şeyle tarif edilebilecek bir şey için bir değer olarak geçirmek zorunda kaldıklarında kafanız karışacaktır int..." --- bunun için burada En Az Sürpriz İlkesi'ne rehberlik ediyoruz. İyi yakalama.
Greg Burghardt

1
Özel aralıkların bunu birimler kütüphanesinin alanından ve bağımlı türlere
jk

6

Null cevabının aksine, bir tamsayı ölçümü tanımlamak için yeterli değilse bir "birim" için bir tür tanımlamak yararlı olabilir. Örneğin, ağırlık genellikle aynı ölçüm sistemi içinde birden fazla birimde ölçülür. "Pound" ve "ons" veya "kilogram" ve "gram" düşünün.

Birim için bir tür tanımlayan daha ayrıntılı bir ölçüm düzeyine ihtiyacınız varsa yararlıdır:

public struct Weight {
    private int pounds;
    private int ounces;

    public Weight(int pounds, int ounces) {
        // Value range checks go here
        // Throw exception if ounces is greater than 16?
    }

    // Getters go here
}

"Yaş" gibi bir şey için çalışma zamanında kişinin doğum tarihine göre hesaplanmasını öneririm:

public class Adult {
    private Date birthDate;

    public Interval getCurrentAge() {
        return calculateAge(Date.now());
    }

    public Interval calculateAge(Date date) {
        // Return interval between birthDate and date
    }
}

2

Aradığınız şey etiketli türler olarak bilinir . "Bu, yaşı temsil eden bir tam sayıdır", "bu aynı zamanda bir tam sayıdır, ancak ağırlığı temsil eder" ve "birini diğerine atayamazsınız" demenin bir yoludur. Bunun, metre veya kilogram gibi fiziksel birimlerden daha ileri gittiğine dikkat edin: Programımda, her ikisi de metre olarak ölçülen, ancak bir tanesini diğeri iş mantığının bakış açısından bir anlam ifade etmiyor.

Scala gibi bazı diller etiketli türleri oldukça kolay bir şekilde destekler (yukarıdaki bağlantıya bakın). Diğerlerinde, kendi sarmalayıcı sınıflarınızı oluşturabilirsiniz, ancak bu daha az uygundur.

Doğrulama, örneğin bir kişinin boyunun "makul" olup olmadığını kontrol etmek başka bir konudur. Bu kodu Adultsınıfınıza (yapıcı veya ayarlayıcılar) veya etiketli tür / sarmalayıcı sınıflarınızın içine koyabilirsiniz . Bir bakıma, böyle bir rol gibi URLya da UUIDyerine getirilen yerleşik sınıflar (diğerleri arasında, örneğin faydalı yöntemler sağlama).

Etiketli türleri veya sarıcı sınıfları kullanmanın aslında kodunuzu daha iyi hale getirmeye yardımcı olup olmayacağı, birkaç faktöre bağlı olacaktır. Nesneleriniz basitse ve çok az alana sahipse, bunları yanlış atama riski düşüktür ve etiketli türleri kullanmak için gereken ek kod çabaya değmeyebilir. Karmaşık yapılara ve çok sayıda alana sahip karmaşık sistemlerde (özellikle birçoğu aynı ilkel türü paylaşıyorsa), aslında yararlı olabilir.

Yazdığım kodda, haritaların etrafından geçersem genellikle sarıcı sınıfları oluştururum. Gibi türler Map<String, String>kendileri çok opaktır, bu yüzden onları anlamlı isimlerle sınıflara sarmak NameToAddressçok yardımcı olur. Tabii ki, etiketli türlerle, Map<Name, Address>tüm harita için yazabilir ve sarmalayıcıya ihtiyacınız yoktur.

Ancak, Dizeler veya Tamsayılar gibi basit türler için, sarma sınıfları (Java) çok fazla sıkıntı buldum. Düzenli iş mantığı o kadar da kötü değildi, ancak bu türleri JSON'a serileştirmek, bunları DB nesnelerine eşlemek vb. İle ilgili bir takım sorunlar ortaya çıktı. ancak bu kodla ilgili ekstra iş ve bakım, bu paketleyicileri kullanarak kazandığınız her şeyi dengeleyecektir. Tabii ki, YMMV ve başka bir sistemde denge farklı olabilir.

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.