Veri doğrulama: ayrılmış sınıf mı değil mi?


16

Doğrulanması gereken çok fazla veri olduğunda, yalnızca doğrulama amacıyla yeni bir sınıf oluşturmalı mıyım yoksa yöntem içi doğrulamaya sadık kalmalı mıyım?

Benim özel örneğim bir turnuvayı ve bir etkinlik / kategori sınıfını tasarlar: Tournamentve Eventbir spor turnuvasını modelleyen ve her turnuvanın bir veya daha fazla kategorisi vardır.

Bu sınıflarda doğrulanacak her türlü şey vardır: oyuncular boş olmalı, benzersiz olmalı, her oyuncunun oynaması gereken maç sayısı, her maçın sahip olduğu oyuncu sayısı, önceden tanımlanmış eşleşmeler ve çok daha fazlasını içeren gerçekten büyük bir vb. karmaşık kurallar.

Sınıfların birbiriyle nasıl entegre olduğu gibi, bir bütün olarak doğrulamam gereken bazı bölümler de vardır. Örneğin, a'nın tek biçimli doğrulaması Playeriyi olabilir, ancak bir etkinliğin iki kez aynı oynatıcısı varsa, bu bir doğrulama hatasıdır.

Peki buna ne dersiniz ?: Model sınıflarımın ayarlayıcılarını ve veri eklemek için benzer yöntemleri kullanırken kesinlikle herhangi bir ön kontrolü unutuyorum ve bunun yerine doğrulama sınıflarının bunu yapmasına izin veriyorum.

Bu yüzden böyle bir şey olacak EventValidatorbir ile Eventbir üyesi olarak ve bir validate()bütün üyelerin kurallarını geçerli kılmak için bütün nesneyi doğrular yöntemi artı tekil yöntemler.

Sonra, geçerli bir nesneyi somutlaştırmadan önce, yasadışı değerleri önlemek için doğrulamayı yürüteceğim.

Tasarımım doğru mu? Farklı bir şey yapmalı mıyım?

Ayrıca, boole dönen doğrulama yöntemleri kullanmalı mıyım? Ya da doğrulama başarısız olursa bir istisna atabilir misiniz? Bana en iyi seçenek boolean döndürme yöntemleri ve nesne örneklendiğinde istisna atmak gibi görünüyor, örneğin:

public Event() {
    EventValidator eventValidator = new EventValidator(this);
    if (!eventValidator.validate()) {
        // show error messages with methods defined in the validator
        throw new Exception(); // what type of exception would be best? should I create custom ones?
    }
}

Yanıtlar:


7

Bir mantık, bir programın yürütülmesi sırasında dinamik olarak değişecekse , kompozisyon yoluyla herhangi bir mantığın devredilmesi uygundur . Açıkladığınız gibi karmaşık doğrulamalar, kompozisyon yoluyla başka bir sınıfa devredilecek kadar iyi bir adaydır.

Doğrulamaların farklı anlarda gerçekleşebileceğini unutmayın.

Örneğinizdeki gibi somut bir doğrulayıcıyı örneklemek kötü bir fikirdir çünkü Etkinlik sınıfınızı söz konusu doğrulayıcıyla eşleştirir.

Herhangi bir DI çerçevesi kullanmadığınızı varsayalım.

Doğrulayıcıyı yapıcıya ekleyebilir veya bir ayarlayıcı yöntemiyle enjekte edebilirsiniz. Bir fabrikadaki bir içerik oluşturucu yönteminin hem Olayı hem de doğrulayıcıyı başlatır ve daha sonra bunu olay oluşturucusuna veya bir setValidator yöntemiyle geçirmesini öneririm.

Açıkçası bir Validator arabirimi ve / veya soyut sınıf yazılmalıdır, böylece sınıflarınız herhangi bir somut validata değil ona bağlıdır.

Doğrulayıcı yönteminin yapıcıda yürütülmesi sorunlu olabilir, çünkü doğrulamak istediğiniz tüm durum henüz yerinde olmayabilir.

Doğrulanabilir bir arabirim oluşturmanızı ve sınıflarınızın uygulamayı gerçekleştirmesini sağlamanızı öneririm, bu arabirimin bir validate () yöntemi olabilir.

Bu şekilde, uygulamanızın üst bileşenleri istek üzerine validate yöntemini çağırır (bu da validator üyesine devredilir).

==> IValidable.java <==

import java.util.List;

public interface IValidable {
    public void setValidator(IValidator<Event> validator_);
    public void validate() throws ValidationException;
    public List<String> getMessages();
}

==> IValidator.java <==

import java.util.List;

public interface IValidator<T> {
    public boolean validate(T e);
    public List<String> getValidationMessages();
}

==> Event.java <==

import java.util.List;

public class Event implements IValidable {

    private IValidator<Event> validator;

    @Override
    public void setValidator(IValidator<Event> validator_) {
        this.validator = validator_;
    }

    @Override
    public void validate() throws ValidationException {
        if (!this.validator.validate(this)){
            throw new ValidationException("WTF!");
        }
    }

    @Override
    public List<String> getMessages() {
        return this.validator.getValidationMessages();
    }

}

==> SimpleEventValidator.java <==

import java.util.ArrayList;
import java.util.List;

public class SimpleEventValidator implements IValidator<Event> {

    private List<String> messages = new ArrayList<String>();
    @Override
    public boolean validate(Event e) {
        // do validations here, by accessing the public getters of e
        // propulate list of messages is necessary
        // this example always returns false    
        return false;
    }

    @Override
    public List<String> getValidationMessages() {
        return this.messages;
    }

}

==> ValidationException.java <==

public class ValidationException extends Exception {
    public ValidationException(String message) {
        super(message);
    }

    private static final long serialVersionUID = 1L;
}

==> Test.java <==

public class Test {
    public static void main (String args[]){
        Event e = new Event();
        IValidator<Event> v = new SimpleEventValidator();
        e.setValidator(v);
        // set other thins to e like
        // e.setPlayers(player1,player2,player3)
        // e.setNumberOfMatches(3);
        // etc
        try {
            e.validate();
        } catch (ValidationException e1) {
            System.out.println("Your event doesn't comply with the federation regulations for the following reasons: ");
            for (String s: e.getMessages()){
                System.out.println(s);
            }
        }
    }
}

Sınıf hiyerarşisi açısından bu önerdiğimden çok farklı değil mi? Doğrulayıcıyı kurucu içinde başlatmak ve çağırmak yerine, yürütme akışında gerektiğinde oluşturulur ve çağrılır, görebildiğim ana fark budur.
dabadaba

Cevabım temel olarak karmaşık doğrulama yapmak için başka bir sınıf oluşturmanın sorun olmaması. Sert bağlantıdan kaçınmanız ve daha fazla esneklik almanız için bazı tavsiyeler ekledim.
Tulains Córdova

Bir listeye veya sözlüğe hata mesajları ekleseydim, içeride mi Validatoryoksa içinde Validablemi olmalı ? Ve bu mesajları ile birlikte nasıl çalışabilirim ValidationException?
dabadaba

1
@dabadaba Liste IValidator uygulayıcılarının bir üyesi olmalıdır, ancak IValidable bu yönteme erişim eklemelidir, böylece uygulayıcılar temsilci atamalıdır. Birden fazla doğrulama mesajının iade edilebileceğini varsaydım. Üzerinde Clic önce düzenlendi n dakika altındaki linke Eğer farklılıkları görebilirsiniz. Yan yana görünümü kullanırsanız daha iyi olur (işaretleme yok).
Tulains Córdova

3
Muhtemelen bilgiçlikçi olmak, ama sanırım gerçek sıfattan Validatableçok daha iyi bir isimValidable
user919426

4

Model sınıflarımın ayarlayıcılarını ve veri eklemek için benzer yöntemleri kullanırken kesinlikle herhangi bir ön kontrolü unutuyorum

İşte sorun bu. İdeal olarak, nesnelerinizin geçersiz bir duruma sahip olmasını önlemelisiniz: Geçersiz durumla somutlaştırmaya izin vermeyin ve ayarlayıcılara ve diğer durum değiştirme yöntemlerine sahip olmanız gerekiyorsa, buraya bir istisna atın.

bunun yerine doğrulama sınıflarının doğrulamayı işlemesine izin verdim.

Bu çelişki değil. Doğrulama mantığı kendi sınıfını garanti edecek kadar karmaşıksa yine de doğrulama yetkisi verebilirsiniz. Ve eğer doğru olduğunu anlarsam, şu anda tam olarak bunu yapıyorsun:

Sonra, geçerli bir nesneyi somutlaştırmadan önce, yasadışı değerleri önlemek için doğrulamayı yürüteceğim.

Hala geçersiz duruma neden olabilecek ayarlayıcılarınız varsa, durumu gerçekten değiştirmeden önce doğruladığınızdan emin olun . Aksi takdirde bir hata mesajınız olur, ancak yine de nesnenizi geçersiz durumda bırakırsınız. Tekrar: nesnelerinizin geçersiz duruma gelmesini engelleyin

Ayrıca, boole dönen doğrulama yöntemleri kullanmalı mıyım? Ya da doğrulama başarısız olursa bir istisna atabilir misiniz? Bana en iyi seçenek boolean döndürme yöntemleri ve nesne örneklendiğinde istisna atmak gibi görünüyor

Doğrulayıcı geçersiz veya geçerli bir girdi beklediğinden, bana göre iyi geliyor, geçersiz girdi bir istisna değildir.

Güncelleme: "Bir bütün olarak sistem" geçersiz hale gelirse, bunun nedeni bazı nesnelerin değişmiş olması (örneğin bir oynatıcı) ve bu nesneye başvuran bazı daha yüksek düzeyli nesnelerin (örneğin bir olay) artık yerine getirilmeyen bir koşula sahip olmasıdır. . Şimdi bir çözüm, oynatıcıda daha yüksek bir seviyede geçersiz bir şey yapabilen doğrudan değişikliklere izin vermemek olacaktır. Bu, değişmeyen nesnelerin yardımcı olduğu yerdir . Sonra değiştirilmiş bir oyuncu farklı bir nesnedir. Bir oyuncu bir etkinlikle ilişkilendirildikten sonra, etkinliği yalnızca olayın kendisinden değiştirebilirsiniz (çünkü referansı yeni bir nesneye değiştirmeniz gerekir) ve hemen doğrulayın.


Örnekleme ve ayarlayıcılarda hiçbir denetim yapılmamasına ve bir kenara bırakma işlemi olarak onaylamaya ne dersiniz? Tulains Córdova gibi bir şey önerdi. Çünkü bir ayarlayıcı veya kurucuda bir istisnayı doğrulamak ve atmak o üye veya nesne için işe yarayabilir, ancak bir bütün olarak sistemin iyi olup olmadığını kontrol etmeye yardımcı olmaz.
dabadaba

@dabadaba Cevabım bir yorum için uzun sürdü, lütfen yukarıdaki güncellemeye bakın
Fabian Schmengler

Değişmez nesneleri kullanmak istemiyorum, derslerimi değiştirebilmek istiyorum. Ve nihai anahtar kelimeden bahsetmedikçe değişmez nesneleri nasıl yapacağımı bilmiyorum. Bu durumda, kısmen ek ayarlayıcılarla etkinlikler ve turnuvalar oluşturduğum için tasarımımı çok değiştirmem gerekecekti (çünkü bu veriler ek ve yapılandırılabilir).
dabadaba

3
Değişmez nesneler yapmak için tüm ayarlayıcıları kaldırın ve değerleri ayarlamak için yalnızca yapıcıları kullanın. İsteğe bağlı veya bir kerede mevcut olmayan verileriniz varsa, oluşturucu desenini kullanmayı düşünün: developer.amd.com/community/blog/2009/02/06/…
Fabian Schmengler 17:16
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.