readonly class design when a non-readonly class is already in place

前端 未结 5 2516
借酒劲吻你
借酒劲吻你 2021-02-19 16:55

I have a class that upon construction, loads it\'s info from a database. The info is all modifiable, and then the developer can call Save() on it to make it Save that informati

5条回答
  •  清酒与你
    2021-02-19 17:42

    I had the same problem to solve when creating an object for user security permissions, that in certain cases must be mutable to allow high-level users to modify security settings, but normally is read-only to store the currently logged-in user's permissions information without allowing code to modify those permissions on the fly.

    The pattern I came up with was to define an interface which the mutable object implements, that has read-only property getters. The mutable implementation of that interface can then be private, allowing code that directly deals with instantiating and hydrating the object to do so, but once the object is returned out of that code (as an instance of the interface) the setters are no longer accessible.

    Example:

    //this is what "ordinary" code uses for read-only access to user info.
    public interface IUser
    {
       string UserName {get;}
       IEnumerable PermissionStrongNames {get;}
    
       ...
    }
    
    //This class is used for editing user information.
    //It does not implement the interface, and so while editable it cannot be 
    //easily used to "fake" an IUser for authorization
    public sealed class EditableUser 
    {
       public string UserName{get;set;}
       List Groups {get;set;}
    
       ...
    }
    
    ...
    
    //this class is nested within the class responsible for login authentication,
    //which returns instances as IUsers once successfully authenticated
    private sealed class AuthUser:IUser
    {
       private readonly EditableUser user;
    
       public AuthUser(EditableUser mutableUser) { user = mutableUser; }
    
       public string UserName {get{return user.UserName;}}
    
       public IEnumerable PermissionNames 
       {
           //GetPermissions is an extension method that traverses the list of nestable Groups.
           get {return user.Groups.GetPermissions().Select(p=>p.StrongName);
       }
    
       ...
    }
    

    A pattern like this allows you to use code you've already created in a read-write fashion, while not allowing Joe Programmer to turn a read-only instance into a mutable one. There are a few more tricks in my actual implementation, mainly dealing with persistence of the editable object (since editing user records is a secured action, an EditableUser cannot be saved with the Repository's "normal" persistence method; it instead requires calling an overload that also takes an IUser which must have sufficient permissions).

    One thing you simply must understand; if it is possible for your program to edit the records in any scope, it is possible for that ability to be abused, whether intentionally or otherwise. Regular code reviews of any usage of the mutable or immutable forms of your object will be necessary to make sure other coders aren't doing anything "clever". This pattern also isn't enough to ensure that an application used by the general public is secure; if you can write an IUser implementation, so can an attacker, so you'll need some additional way to verify that your code and not an attacker's produced a particular IUser instance, and that the instance hasn't been tampered with in the interim.

提交回复
热议问题