在我的应用程序中,用户可以执行许多不同的操作,例如(书/库的例子只是为了让它更清晰;我的应用程序中的动作要复杂得多):
我的应用程序还包含一些安全检查,可以阻止其中一些操作。 这些检查与动作没有直接关系,而是与动作分开表示概念,例如:
目前,不同的行动(租赁,阅读......)包含如下代码:
void readBook (Book &book)
{
if (!checkSecurity (book, OPENBOOK|SEEBOOK)) return;
...
}
void writeBook (Book &book)
{
if (!checkSecurity (book, MODIFYBOOK)) return;
...
}
这种方法很难以动态方式添加新类型的安全检查。例如。动态插件可能会添加额外的安全检查。
我目前正在处理的解决方案是,不同的操作会调用如下所示的界面:
class ISecurityChecker
{
public:
bool isReadBookAllowed() const = 0;
bool isWriteBookAllowed() const = 0;
...
};
复合类然后实现此接口。可以将此接口的其他实现添加到组合中。这样,应用程序的插件或动态部分可以在需要时添加新的安全检查。
这种方法也有一些缺点。主要问题是界面变得非常大。我将看看是否可以合并一些接口方法(因为它们基本上执行相同或类似的操作),但它不确定这是否可行。
任何其他改善去耦的方法?或者关于如何改进这种设计的建议?
答案 0 :(得分:3)
我可以提出两种可能的改变......以及一条评论。
首先,尽可能向下推动您的原始支票。抛出异常以传达授权错误。
void readBook (Book &book)
{
// readbook doesn't need to perform any checks of it's own.
// the more primitive actions will scream and yell if the user
// isn't allowed to perform sub-action X.
openBook(book);
BookData d = getBookData(book);
// etc.
}
void openBook (Book &book)
{
if (!checkSecurity (book, OPENBOOK))
throw new SecurityException("User X is not allowed to open this book!");
// etc.
}
BookData getBookData (Book &book)
{
if (!checkSecurity (book, SEEBOOK))
throw new SecurityException("User X is not allowed to read this book's data!");
// etc.
}
其次,将您的安全操作映射到实际操作。如果您愿意,您甚至可以在数据中执行此操作。例如......
class Security {
// this check get tricky.
// if an "real action" isn't listed anywhere, does the user have implicit permission?
// (i'm assuming not, for this example.)
public static Check(String realAction, Boolean requireAll = true) {
Int32 required = 0;
Int32 userHas = 0;
foreach (KeyValuePair<String, List<String>> pair in Actions) {
if (pair.Value.Contains(realAction))
{
required++;
if (Security.CurrentUser.Actions.Contains(pair.Key))
{
userHas++;
}
}
}
if (requireAll)
{
return userHas > 0 && userHas == required;
}
else
{
return userHas > 0;
}
}
// hardcoded here, but easily populated from a database or config file
public static Dictionary<String, List<String>> Actions {
{"OpenBook", new List<String>() { "readBook", "writeBook" }},
{"SeeBook", new List<String>() { "readBook", "writeBook" }}
}
}
void readBook(Book &book) {
if (!Security.Check("readBook")) return false;
// etc.
}
这里的Check()
方法接受requireAll
参数,但是映射本身可以很容易地更新为“坚持”或“更喜欢”出现在他们隐含的“真实行为”中。
我的评论:不要过度详细说明您的安全性。一些安全规则意味着其他规则本身可能毫无意义。例如,READBOOK
和WRITEBOOK
都意味着OPENBOOK
的能力,而OPENBOOK
本身可能毫无意义。虽然如果用户OPENBOOK
或WRITEBOOK
没有SEEBOOKCOVER
和SEEBOOKINSEARCHRESULTS
之类的内容或者其他任何内容,用户似乎很愚蠢,我建议只有相关的权限在图书阅读时间是READBOOK
。
答案 1 :(得分:1)
使用命令查询分离(CQS)方法。一般的想法是定义一个这样的命令类:
public class Command
{
public Command(Action<object> act, Func<object, bool> canExecute)
{
this.act = act;
this.canExecute = canExecute;
}
private Action<object> act;
private Func<object, bool> canExecute;
public void Execute()
{
if (CanExecute())
{
act(this);
}
else
{
throw new InvalidOperationException("Command cannot be executed");
}
}
public bool CanExecute()
{
if (this.canExecute != null)
{
return this.canExecute(this);
}
else return true;
}
}
然后在页面中,您使用Command.Execute()
而不是常规book.Write
/ book.Print
等。
用法:
Command readBookCommand = new Command(
k =>
{
book.Read();
},
l =>
{
CanReadBook();
}
);
readBookCommand.Execute();
可能有更好的CQS实施。这只是一个简单的例子。