如果我有一个Policy
,并且这个Policy
应该由Sections
(固定数字)组成。
我的版块是4个预定义的版块:
每个Section
的固定属性不同于其他部分的属性。
如果可以用类比来说明它:
例如:
name,list of work-times
。numberofhours, reason, excuse_type
。注意:基于领域专家的解释:他希望每个部分(作为草稿)都进行保存操作,以便他可以更新该部分 除非政策尚未提交,并且整个操作提交 政策,因此在政策提交后,任何人都无法更新或删除此政策 政策或其部分。 (任何必需的更新=定义新政策)
现在,我要设计Policy
,Section
和its content
。但我被困住了。
首先,我认为我可以将Policy
设计为Entity(聚合根),并创建four
类,每个Section
一个类,并从Section base class(Id,name)
继承它们,然后Policy
包含Section
的列表。
其次,我指导我的思想以以下方式概括本节内容:
我将创建:
ISection
:SectionType
,SectionRule
然后我将创建参考表SectionRules:
EX:
rule-key |default-value|operators|section-type|value-type|rule-type
NumOfhoursInMonth| 5:00 | = | 2 | String |Assignment
AvailableExcuses |2:00,2:30 | IN | 2 | List<String>|Relational
注释:
1
是Excuses
当用户启动Policy
时,我将遍历ref表以一种形式列出规则,以便他可以更改default values
并将其保存在
根据其类型的部分如下:
Id |Name |Rule |section-type
1 |Excuses |NumOfhoursInMonth <= 6 | 2
我现在面临两个问题。
NumOfExcuses'hoursInMonth Should be less than or equal 6:00
根据第一个规则,但是如何防止用户
在设置第二条规则时违反了此规则
AvailableExcuses IN(5:00,7:00)
!现在,我应该防止用户
添加一个大于6
的数字,因为第一个规则限制了
第二个 ?第二条规则与第一条规则不一致,因为列表包含(07:00)
,并且第一条规则指出totalExcuseshoursInMonth <= 06:00 hours
我的方向正确吗?我可以针对我的情况获得一些建议吗?
答案 0 :(得分:3)
我不完全确定哪种设计最合适,您肯定需要经历多次模型迭代才能满意,但是我认为问题的核心是假设规则的制定和查找冲突的规则。规范模式基本上包括使规则成为模型的一等公民,而不是仅通过条件语言构造来表达规则。
有很多方法可以实现该模式,但这是一个示例:
在我设计的其中一个系统 1 中,我设法重用了一组相同的规范集来强制执行命令和查询的授权规则并强制执行和描述业务规则。
例如,您可以在规范中添加用于描述其约束的describe(): string
方法,或可以将其转换为SQL的toSql(string mainPolicyTableAlias)
方法。
例如(伪代码)
someSpec = new SomeRule(...).and(new SomeOtherRule(...));
unsatisfiedSpec = someSpec.remainderUnsatisfiedBy(someCandidate);
errorMessage = unsatisfiedSpec.describe();
但是,直接在规范上实施此类操作可能会污染各种应用程序/基础架构。为了避免这种污染,您可以使用,它可以让您在正确的层中对各种操作进行建模。这种方法的缺点是,每次添加一种新的具体规范时,您都必须更改所有访问者。
#1为此,我必须实现上述论文中描述的其他规范操作,例如remainderUnsatisfiedBy
等。 < / p>
自从我用C#编程以来已经有一段时间了,但是我认为C#中的可以非常方便地实现规范并将其转换为多种表示形式。
验证策略每个部分中不同规则之间的相关性
我不能完全确定您的想法,但是通过在规范中添加诸如conflictsWith(Spec other): bool
之类的操作,您可以实现一种冲突检测算法,该算法将告诉您一个或多个规则是否冲突
例如,在下面的示例中,这两个规则可能会冲突,因为这两个规则永远不可能是真实的(伪代码):
rule1 = new AttributeEquals('someAttribute', 'some value');
rule2 = new AttributeEquals('someAttribute', 'some other value');
rule1.conflictsWith(rule2); //true
总而言之,您的整个模型肯定会比这复杂得多,并且您将必须找到描述规则并将它们与正确的组件相关联的正确方法。您甚至可能希望将某些规则与适用性规范链接在一起,以便它们仅在满足某些特定条件时适用,并且您可能有许多各种规范候选类型,例如Policy
,Section
或{{1} },因为某些规则可能需要应用于整个SectionAttribute
,而其他类型的规则必须根据特定部分的属性进行解释。
希望我的回答会引发一些想法,使您步入正轨。我还建议您查看现有的验证框架和规则引擎,以获取更多建议。另请注意,如果您希望所有规则和Policy
的状态始终保持一致,则很有可能将Policy
设计为由所有部分和规则组成的大型集合。如果由于性能原因或并发冲突(例如许多用户编辑同一策略的不同部分)而以某种方式无法实现或不合需要(例如,许多用户编辑同一策略的不同部分),那么也许您将被迫分解大型聚合并使用最终的一致性。
您当然还必须考虑当新规则使现有状态无效时需要执行的操作。也许您可能想同时强制更改规则和状态,或者可以实现状态验证指示器以将当前状态的某些部分标记为无效,等等。
1-您能否解释一下有关describe(),toSql(string mainPolicyTableAlias)的更多信息,我不理解这些功能背后的意图。
好吧,Policy
将对规则进行描述。如果您需要i18n支持
或更多地控制消息,您可能想要使用访问者来代替,也许您还想要一个功能,可以在其中使用模板消息来覆盖自动描述,等等。describe
方法是相同的,但是会生成例如,可以在toSql
条件内使用的内容。
WHERE
这是一个很大的缺点!请问如何克服这个问题。
直接在对象上支持行为可以轻松添加新对象,但是在使用访问者模式时更难添加新行为,因此可以轻松添加新行为,但是很难添加新类型。那就是众所周知的expression trees。
如果您可以找到不太可能针对所有特定类型更改的通用抽象表示形式,则可以缓解该问题。例如,如果您想绘制许多类型的new Required().describe() //required
new NumericRange(']0-9]').if(NotNullOrEmpty()).describe() //when provided, must be in ]0-9] range
,例如Polygon
,Triangle
等,则最终可以将它们全部表示为一系列有序点。规范当然可以分解为Expression Problem(Expression),但这不会神奇地解决所有翻译问题。
这是JavaScript和HTML的示例实现。请注意,某些规范的实现非常幼稚,无法与undefined / blank / null值配合使用,但是您应该明白这一点。
Square
class AttrRule {
isSatisfiedBy(value) { return true; }
and(otherRule) { return new AndAttrRule(this, otherRule); }
or(otherRule) { return new OrAttrRule(this, otherRule); }
not() { return new NotAttrRule(this); }
describe() { return ''; }
}
class BinaryCompositeAttrRule extends AttrRule {
constructor(leftRule, rightRule) {
super();
this.leftRule = leftRule;
this.rightRule = rightRule;
}
isSatisfiedBy(value) {
const leftSatisfied = this.leftRule.isSatisfiedBy(value);
const rightSatisfied = this.rightRule.isSatisfiedBy(value);
return this._combineSatisfactions(leftSatisfied, rightSatisfied);
}
describe() {
const leftDesc = this.leftRule.describe();
const rightDesc = this.rightRule.describe();
return `(${leftDesc}) ${this._descCombinationOperator()} (${rightDesc})`;
}
}
class AndAttrRule extends BinaryCompositeAttrRule {
_combineSatisfactions(leftSatisfied, rightSatisfied) { return !!(leftSatisfied && rightSatisfied); }
_descCombinationOperator() { return 'and'; }
}
class OrAttrRule extends BinaryCompositeAttrRule {
_combineSatisfactions(leftSatisfied, rightSatisfied) { return !!(leftSatisfied || rightSatisfied); }
_descCombinationOperator() { return 'or'; }
}
class NotAttrRule extends AttrRule {
constructor(innerRule) {
super();
this.innerRule = innerRule;
}
isSatisfiedBy(value) {
return !this.innerRule;
}
describe() { return 'not (${this.innerRule.describe()})'}
}
class ValueInAttrRule extends AttrRule {
constructor(values) {
super();
this.values = values;
}
isSatisfiedBy(value) {
return ~this.values.indexOf(value);
}
describe() { return `must be in ${JSON.stringify(this.values)}`; }
}
class CompareAttrRule extends AttrRule {
constructor(operator, value) {
super();
this.value = value;
this.operator = operator;
}
isSatisfiedBy(value) {
//Unsafe implementation
return eval(`value ${this.operator} this.value`);
}
describe() { return `must be ${this.operator} ${this.value}`; }
}
const rules = {
numOfHoursInMonth: new CompareAttrRule('<=', 6),
excuseType: new ValueInAttrRule(['some_excuse_type', 'some_other_excuse_type']),
otherForFun: new CompareAttrRule('>=', 0).and(new CompareAttrRule('<=', 5))
};
displayRules();
initFormValidation();
function displayRules() {
const frag = document.createDocumentFragment();
Object.keys(rules).forEach(k => {
const ruleEl = frag.appendChild(document.createElement('li'));
ruleEl.innerHTML = `${k}: ${rules[k].describe()}`;
});
document.getElementById('rules').appendChild(frag);
}
function initFormValidation() {
const form = document.querySelector('form');
form.addEventListener('submit', e => {
e.preventDefault();
});
form.addEventListener('input', e => {
validateInput(e.target);
});
Array.from(form.querySelectorAll('input')).forEach(validateInput);
}
function validateInput(input) {
const rule = rules[input.name];
const satisfied = rule.isSatisfiedBy(input.value);
const errorMsg = satisfied? '' : rule.describe();
input.setCustomValidity(errorMsg);
}
form > label {
display: block;
margin-bottom: 5px;
}
input:invalid {
color: red;
}
答案 1 :(得分:1)
似乎您想要一个对象模型,大概是用于自定义CMS的,将用于呈现表单:
一些注意事项:
从您的问题看来,至少有两个角色:
设计注意事项
我的建议
这是一个很好的开始,而真正尝试使这100%提前完成是浪费时间。希望这可以帮助您摆脱困境。