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:权限控制类的设计的主要内容,如果未能解决你的问题,请参考以下文章