OJ建成计划1:权限控制类的设计

Posted caturra

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OJ建成计划1:权限控制类的设计相关的知识,希望对你有一定的参考价值。

花了一个下午+晚上来写权限控制类,删改了很多次(感谢V站大哥们的提议),目前个人感觉挺满意的,主要是以下几点花了点工夫

1.设计模式上的应用,factory+observer+strategy(反射实现),不过factory没写什么特别的,就加多个缓存

2.权限设计的随意,对于添加标签,外部只需给出一个描述用的字符串即可实现使用方和权限方的交互,并且可用于任意observer对象的验证

3.内部对象的安全发布(伪),保证了完成功能的同时不会逸出任意可变对象,不可变对象直接用public即可获得

4.低耦合,除了classes需要手动配置目前没发现其它强制的依赖

PS.欠缺测试,目前只是梳理思路

PPS.该版本不代表最终版本

首先几个model一并放出

Contest类

package com.noresp.oj.model;

import com.noresp.oj.model.AuthorityUtils.AuthorityObserver;

import java.util.Set;

public class Contest implements AuthorityObserver {

    private String ContestID;
    private String title;
    private String board;
    private String descrption;
    private Set<Authority> authorities;

    public void addSubject(Authority subject) {
        authorities.add(subject);
    }

    public void removeSubject(Authority subject) {
        authorities.remove(subject);
    }
}

Problem类

package com.noresp.oj.model;

import com.noresp.oj.model.AuthorityUtils.AuthorityObserver;

import java.util.Set;

public class Problem implements AuthorityObserver {

    private String problemID;
    private String title;
    private String descrption;
    private Set<Authority> authorities;

    public void addSubject(Authority subject) {
        authorities.add(subject);
    }

    public void removeSubject(Authority subject) {
        authorities.remove(subject);
    }
}

User类

package com.noresp.oj.model;

import com.noresp.oj.model.AuthorityUtils.AuthorityObserver;

import java.util.Set;

public class User implements AuthorityObserver {

    private String UserID;
    private String Username;
    private String password;
    private String email;
    private Set<Authority> authorities;

    public void addSubject(Authority subject) {
        authorities.add(subject);
    }

    public void removeSubject(Authority subject) {
        authorities.remove(subject);
    }
}

以下是两个接口

package com.noresp.oj.model.AuthorityUtils;

import com.noresp.oj.model.Authority;

import java.util.Set;

public interface AuthorityObserver { // 尽量别用abstract,对于User等类可能需要其它扩展
    void addSubject(Authority subject);
    void removeSubject(Authority subject);
}
package com.noresp.oj.model.AuthorityUtils;

import com.noresp.oj.model.AuthorityUtils.AuthorityObserver;

public interface AuthoritySubject {
    void registerObserver(AuthorityObserver observer);
    void removeObserver(AuthorityObserver observer);
//    void notifyObservers();
}

接下来是最重要的权限类,看注释吧

package com.noresp.oj.model;

import com.noresp.oj.model.AuthorityUtils.AuthorityDispatcher;
import com.noresp.oj.model.AuthorityUtils.AuthorityObserver;
import com.noresp.oj.model.AuthorityUtils.AuthoritySubject;

import java.util.*;

public final class Authority implements AuthoritySubject {

    private static Map<String, Authority> authorityFactoryCache = new HashMap<>();

    private final Integer authorityID; // 内部使用,不必对外给出
    public final String description; // 使用public,省去getter
    private final Set<AuthorityObserver> Observers;

    /**
     * 用于标记使用权限的类
     */
    private static final Class[]
            observerClasses = new Class[] {User.class,Problem.class,Contest.class};
    /**
     * 用于给使用权限的类进行分Set标记,每一个类对应一个Set
     * 避免出现Set<User> usedUser / Set<Contest> usedContest / Set<Problem> usedProblem等多个高度耦合的类
     */
    private final Map<Class,Set<? super AuthorityObserver>> observerDispatcher;

    private Authority() {
        authorityID = null;
        description = null;
        Observers = null;
        observerDispatcher = null;
    }

    private Authority(Integer authorityID,String description /* ,Object...objs */ ) {
        this.authorityID = authorityID;
        this.description = description;
        Observers = new HashSet<>();
        observerDispatcher = new HashMap<>();
        for(Class clazz : observerClasses) {
            observerDispatcher.put(clazz,new HashSet<>());
        }
        // 以上final均确保可见性,防止后续遍历过程中出现存在引用但未初始化的现象(已弃用)
    }

    /**
     * 使用Factory生成权限 + Observer订阅实现交互
     * @param description 权限的可读形式
     * @param bindObservers 如果有需要订阅的对象,则可以在此添加
     * @return 返回已缓存或者新建的权限对象
     */
    public static Authority AuthorityFactory(String description, AuthorityObserver...bindObservers) {
        Authority authority = authorityFactoryCache.get(description); // 带有缓存,关爱内存
        if(authority == null) {
            authority = new Authority(authorityFactoryCache.size(),description);
            authorityFactoryCache.put(description,authority);
        }
        for(AuthorityObserver observer : bindObservers) {
            authority.registerObserver(observer);
            observer.addSubject(authority);
            //讲道理,应该是由Subject通知Observer,然后Observer自己调用方法
            //但是接口要求public,这样就意义不大了
            //因此最后验证权限还要双方确认才行

        }
        return authority;
    }

    public void registerObserver(AuthorityObserver observer) {
        Observers.add(observer);
        Set<? super AuthorityObserver> observerDispatched =
                observerDispatcher.get(observer.getClass());
        observerDispatched.add(observer);


        /*
        if(observer instanceof Contest) {
            usedContest.add((Contest)observer);
        } else if(observer instanceof Problem) {
            usedProblem.add((Problem)observer);
        } else if(observer instanceof User) {
            usedUser.add((User)observer);
        }
        */
    }

    public void removeObserver(AuthorityObserver observer) {
        Observers.remove(observer);
        Set<? super AuthorityObserver> observerDispatched =
                observerDispatcher.get(observer.getClass());
        observerDispatched.remove(observer);
    }

//    public String getDescription() {
//        return description;
//    }

    public static String[] getDescriptions() {
        return (String[])authorityFactoryCache.keySet().toArray();
    }

    /**
     * 删除该权限的所有引用
     * 达到删除权限的目的
     */
    public static void delete(String description) {
        Authority authority = authorityFactoryCache.get(description);
        delete(authority);
    }

    public static void delete(Authority authority) {
        if(authority == null) return;
        for(AuthorityObserver observer : authority.Observers) {
            authority.removeObserver(observer);
            observer.removeSubject(authority);
        }
        for(Set set : authority.observerDispatcher.values()) {
            set.clear();
        }
        authority.observerDispatcher.clear();
        authorityFactoryCache.remove(authority);
        // 除了Observer还要从Dispatcher回收节点
        // 如果是AuthorityDispatcher单独设置为一个类,这样又要暴露接口,
        // 因此直接用在一个大的类里面了


    }

    // 注意几个Set不可暴露出getter,防止逸出
}

以上是关于OJ建成计划1:权限控制类的设计的主要内容,如果未能解决你的问题,请参考以下文章

Java类的设计----访问控制

设计模式

设计模式

20165305 苏振龙《Java程序设计》第四周学习总结

漫谈权限控制设计

面向对象的设计模式