Alıcıları kullanarak sabitlemekten kaçınmak iyi bir uygulama mıdır?


25

Sınıf dışında kullanılan sabitlerin alıcılar tarafından değiştirilmesi iyi bir uygulama mıdır?

Bir örnek olarak, daha iyi kullanmaktır if User.getRole().getCode() == Role.CODE_ADMINya if User.getRole().isCodeAdmin()?

Bu, bu sınıfa yol açacaktır:

class Role {
    constant CODE_ADMIN = "admin"
    constant CODE_USER = "user"

    private code

    getRoleCode() {
       return Role.code
    }

    isCodeAdmin () {
       return Role.code == Role.CODE_ADMIN
    }

    isCodeUser () {
       return Role.code == Role.CODE_USER
    }
}

15
Gibi bir şey kullanmayı tercih ederim User.HasRole(Role.Admin).
KodlarInChaos


4
Sakın Sorma ilkesini kontrol edin .
Andy

Ben öncül soruyu soruyorum: User.getRole().getCode()zaten hoş olmayan bir okuma, bir Rolü Kodla karşılaştırmak onu daha da usulsüz hale getiriyor.
msn

Yanıtlar:


47

Öncelikle, böyle entity.underlyingEntity.underlyingEntity.method()bir şey yapmanın Demeter Yasasına göre kod kokusu olarak kabul edildiğini lütfen unutmayın . Bu şekilde, tüketiciye çok sayıda uygulama detayı sunarsınız. Ve böyle bir sistemin genişletilmesi veya değiştirilmesi için her ihtiyaç çok acıtacaktır.

Bu nedenle, size CodesInChaos'un yorumuna göre bir yöntem HasRoleveya IsAdminyöntem kullanmanızı Useröneririm. Bu şekilde, rollerin kullanıcı üzerinde nasıl uygulandığı, tüketici için uygulama detayı olarak kalır. Ayrıca kullanıcıya, rolünün ayrıntılarını sormak yerine rolünün ne olduğunu sormak ve ardından buna dayanarak karar vermek daha doğaldır.


Lütfen stringgerekli olmadıkça s kullanmaktan kaçının . içeriği nameiyi bir örnek stringolduğundan, değişkene iyi bir örnektir . Öte yandan, rolederleme zamanında iyi bilinen iki ayrı değerin olduğu bir şey varsa, güçlü yazı yazmayı daha iyi kullanmalısınız. Numaralandırma türü devreye giriyor.

Karşılaştırmak

public bool HasRole(string role)

ile

public enum Role { Admin, User }

public bool HasRole(Role role)

İkinci vaka, etrafta neler geçirmem gerektiği konusunda daha fazla fikir veriyor. Ayrıca string, rol sabitleriniz hakkında hiçbir fikrimin olmaması durumunda hatalı bir şekilde geçersiz kalmamı engelliyor .


Bundan sonra rolün nasıl görüneceğine dair karar. Doğrudan kullanıcıya depolanan enum'u kullanabilirsiniz:

public enum Role
{
    Admin,
    User
}

public class User
{
    private Role _role;

    public bool HasRole(Role role)
    {
        return _role == role;
    }

    // or
    public bool IsAdmin()
    {
        return _role == Role.Admin;
    }
}

Öte yandan, rolünüzün bir davranışa sahip olmasını istiyorsanız, türünün nasıl karar verildiğinin ayrıntılarını kesinlikle tekrar gizlemesi gerekir:

public enum RoleType
{
    User,
    Admin
}

public class Role
{
    private RoleType _roleType;

    public bool IsAdmin()
    {
        return _roleType == RoleType.Admin;
    }

    public bool IsUser()
    {
        return _roleType == RoleType.User;
    }

    // more role-specific logic...
}

public class User
{
    private Role _role;

    public bool IsAdmin()
    {
        return _role.IsAdmin();
    }

    public bool IsUser()
    {
        return _role.IsUser();
    }
}

Bununla birlikte, bu oldukça ayrıntılı ve karmaşıklık her rol ekleme ile birlikte artacaktır - genellikle Demeter Yasasına tam olarak uymaya çalıştığınızda genellikle kod sona erer. Modellenmekte olan sistemin somut gereksinimlerine dayanarak tasarımı geliştirmelisiniz.

Sorunuza göre, doğrudan enum ile ilk seçeneği ile gitmek daha iyi olur sanırım User. Daha fazla mantığa ihtiyacınız olursa Role, ikinci seçenek bir başlangıç ​​noktası olarak düşünülmelidir.


10
Keşke insanlar bunu her yerde yapabilseydi. Aksi bir özelliğe özgü bir özellik üzerinde bir alıcının olması, başka bir şeye eşit olup olmadığını kontrol etmek uğruna çok korkunç bir uygulamadır.
Andy

2
Re "example.property.property.method () ..." Bu akışkan bir şey değil mi?
Peter Mortensen,

2
@PeterMortensen Akıcı bir arayüzden farklı. Yazıdaki akıcı bir arayüz, Xkesinlikle fonksiyon çağrıları gibi karakter dizileri yapmanızı sağlar X.foo().bar().fee().... Bu akıcı arayüzde, foo, bar ve fee, hepsi sınıf içinde Xbir nesne döndüren işlevler olacaktır X. Ancak bu örnekte belirtilen 'example.property.property.method () için iki propertyçağrı aslında ayrı sınıflarda olacaktır. Buradaki sorun, düşük seviyeli ayrıntılara ulaşmak için birkaç soyutlama katmanından geçmenizdir.
Shaz

10
İyi cevap, ama Demeter Yasası nokta sayma alıştırması değil. instance.property.property.method()mutlaka bir ihlal veya hatta bir kod kokusu değildir; Bu, nesnelerle mi yoksa veri yapılarıyla mı çalıştığınıza bağlıdır. Node.Parent.RightChild.Remove()Muhtemelen bir LoD ihlali değildir (diğer nedenlerle koklamakla birlikte). var role = User.Role; var roleCode = role.Code; var isAdmin = roleCode == ADMIN;"kesinlikle tek nokta kullandım" gerçeğine rağmen neredeyse kesinlikle bir ihlal.
Carl Leth

1
Bunun instance.property.property.method()LoD'nin ihlali olduğunu anlıyorum, ancak OP'nin instance.method().method()iyi olması gereken bir şey var. Son örneğinizde, o kadar çok kazan plakası var Userki, sadece bir cephe olarak hizmet veriyor Role.
Bergi

9

Saklanan kodun yönetici kodu olup olmadığını kontrol etmek için bir işlevi olması çok tuhaf görünüyor. Gerçekten bilmek istediğin, o kişinin yönetici olup olmadığı. Yani eğer sen sabitleri ortaya çıkarmak istemiyoruz, o zaman da bir kod olduğunu teşhir etmemelidir ve yöntemler IsAdmin () ve IsUser () diyoruz.

Yani, "eğer User.getRole (). GetCode () == Role.CODE_ADMIN" gerçekten bir kullanıcının bir yönetici olduğunu kontrol etmek için bir avuçtır. Bir geliştiricinin bu satırı yazmayı hatırlaması gereken kaç şey var? Bir kullanıcının bir rolü, bir rolü bir kodu ve Rol sınıfının kodlar için sabitleri olduğunu hatırlaması gerekir. Bu tamamen uygulama ile ilgili birçok bilgi.


3
Daha da kötüsü: Bir kullanıcının her zaman tam olarak bir rolü vardır, ne daha az ne de az.
Deduplicator

5

Başkalarının daha önce yayınladıklarına ek olarak, sabitin doğrudan kullanılmasının başka bir sakıncası olduğunu aklınızdan çıkarmamalısınız: Bir şey kullanıcı haklarını nasıl ele aldığınızı değiştirirse, tüm bu yerlerin de değiştirilmesi gerekir.

Ve geliştirmek için korkunç yapar. Belki bir noktada süper kullanıcı türüne sahip olmak istersiniz ki bu açıkça yönetici haklarına sahiptir. Kapsülleme ile, temel olarak eklemek için bir astar.

Sadece kısa ve temiz değil, kullanımı ve anlaşılması da kolay. Ve - belki de en önemlisi - yanlış anlaşmak zor.


2

Sabitlerden kaçınmak ve bir yöntem isFoo()vb. Önerilerine büyük ölçüde katılıyorum .

Bu sabitlerin yüzlerceleri varsa ve çağrılar az kullanılırsa, yüzlerce isConstant1, isConstant2, yöntemleri yazma çabasına değmeyebilir. Bu özelde, olağandışı durumda, sabitleri kullanmak makul.

Enums kullanmanın ya da hasRole()yüzlerce yöntem yazma gereksinimini ortadan kaldırdığını , bu nedenle mümkün olan tüm dünyanın en iyisi olduğunu unutmayın.


2

Sunulan seçeneklerden birinin temelde yanlış olduğunu sanmıyorum.

Gördüğüm kadarıyla açıkça yanlış çağıracağım bir şey önermediniz: Rol kodlarını Rol sınıfı dışındaki fonksiyonlarda kodlamak. Yani:

if (user.getRole().equals("Administrator")) ...

Kesinlikle yanlış olduğunu söyleyebilirim. Bunu yapan programları gördüm ve sonra gizemli hatalar aldım çünkü birisi dizgiyi yanlış yazmış. Fonksiyonu "Stok" için kontrol ederken bir programcının "stok" yazdığını bir kez hatırlıyorum.

100 farklı rol olsaydı, olası her birini kontrol etmek için 100 işlev yazmak konusunda isteksiz davranırdım. Muhtemelen ilk işlevi yazıp ardından 99 kez kopyalayıp yapıştırarak onları yaratırsınız ve bu 99 kopyadan birinde testi güncellemeyi unutacağınız ya da bir kez ayrılacağınıza ne kadar bahse gireceğinize inanırsınız. Listeyi koştun, şimdi

public bool isActuary() { return code==Role.ACTUARY; }
public bool isAccountant() { return code==Role.ACTUARY; }
... etc ...

Şahsen ben de çağrı zincirlerinden kaçınırdım. Yazmayı tercih ederim

if (user.getRole().equals(Role.FOOBATER))

sonra

if (user.getRole().getRoleCode()==Role.FOOBATER_CODE)

ve bu noktada neden not yazmak:

if (user.hasRole(Role.FOOBATER))

Sonra Kullanıcı sınıfının bir rolü nasıl kontrol edeceğini bilmesini sağlayın.

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.