Any*_*are 6 c# oop enums domain-driven-design
如何在继承自EnumerationWith限制的类中实现多个选择?
如果我有五种计划类型:
前两个选项是对(Fixed vs Rotated)后两个选项(FullTime vs PartTime)是对的,我的意思是计划不能fixed与rotated在同一时间或fulltime and parttime在同一时间.但它可能是Fixed and FullTime例如.
固定工作时间表,包括每周工作的相同小时数和工作天数,并且一旦雇主和工人商定了小时数和天数,就会保持一致.
灵活的工作时间表,员工和雇主一起工作,以确定他们能够承诺的一周中的小时数和天数.全职工作时间表,通常需要每周37-40小时的承诺.由于工作时间长,具有全职时间表的职业有资格获得工作福利.这些福利包括休假,假期和疾病,健康保险和不同的退休计划选择.
兼职工作时间表,是任何不及全职工作的时间表.
轮换工作时间表,使员工在一天或一周,周转和夜班之间循环.这个循环有助于在所有员工之间分配不同的班次,这样就不会有人因为不太理想的时间而陷入困境.
所以我做了以下事情:
public class Schedule
{
public Schedule()
{
}
private ICollection<ScheduleDetail> _assignedWeeks;
public int Id { get; set; }
public string Name { get; set; }
public int WorkingGroupId { get; set; }
public ScheduleType ScheduleType { get; set; }
public bool IsFixed { get; }
public bool IsFlexible { get; }
public bool IsFullTime { get; }
public ICollection<ScheduleDetail> AssignedWeeks { get => _assignedWeeks; set => _assignedWeeks = value; }
}
Run Code Online (Sandbox Code Playgroud)
public abstract class ScheduleType : Enumeration
{
protected ScheduleType(int value, string displayName) : base(value, displayName)
{
}
public static readonly ScheduleType Fixed
= new FixedType();
public static readonly ScheduleType Flexible
= new FlexibleType();
public static readonly ScheduleType FullTime
= new FullTimeType();
public static readonly ScheduleType PartTime
= new PartTimeType();
public static readonly ScheduleType Rotated
= new RotatedType();
private class FixedType : ScheduleType
{
public FixedType() : base(1, "Fixed Work Schedule")
{
}
}
private class FlexibleType : ScheduleType
{
public FlexibleType() : base(2, "Flexible Work Schedule")
{
}
}
private class FullTimeType : ScheduleType
{
public FullTimeType() : base(3, "Full Time Work Schedule")
{
}
}
private class PartTimeType : ScheduleType
{
public PartTimeType() : base(4, "Part Time Work Schedule")
{
}
}
private class RotatedType : ScheduleType
{
public RotatedType() : base(5, "Rotated Work Schedule")
{
}
}
}
Run Code Online (Sandbox Code Playgroud)
public abstract class Enumeration : IComparable
{
private readonly int _value;
private readonly string _displayName;
protected Enumeration()
{
}
protected Enumeration(int value, string displayName)
{
_value = value;
_displayName = displayName;
}
public int Value
{
get { return _value; }
}
public string DisplayName
{
get { return _displayName; }
}
public override string ToString()
{
return DisplayName;
}
public static IEnumerable<T> GetAll<T>() where T : Enumeration, new()
{
var type = typeof(T);
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
foreach (var info in fields)
{
var instance = new T();
var locatedValue = info.GetValue(instance) as T;
if (locatedValue != null)
{
yield return locatedValue;
}
}
}
public override bool Equals(object obj)
{
var otherValue = obj as Enumeration;
if (otherValue == null)
{
return false;
}
var typeMatches = GetType().Equals(obj.GetType());
var valueMatches = _value.Equals(otherValue.Value);
return typeMatches && valueMatches;
}
public override int GetHashCode()
{
return _value.GetHashCode();
}
public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
{
var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value);
return absoluteDifference;
}
public static T FromValue<T>(int value) where T : Enumeration, new()
{
var matchingItem = parse<T, int>(value, "value", item => item.Value == value);
return matchingItem;
}
public static T FromDisplayName<T>(string displayName) where T : Enumeration, new()
{
var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName);
return matchingItem;
}
private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration, new()
{
var matchingItem = GetAll<T>().FirstOrDefault(predicate);
if (matchingItem == null)
{
var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
throw new ApplicationException(message);
}
return matchingItem;
}
public int CompareTo(object other)
{
return Value.CompareTo(((Enumeration)other).Value);
}
}
Run Code Online (Sandbox Code Playgroud)
因此,根据用户对特定选项或选项集的选择,我必须调用一个方法来(IsFixed,...)在Schedule类中设置标志来控制scheduledetails类(固定和旋转)和小时数(全天和兼职)
我会对任何建议或建议表示感谢?
Boz*_*eff 10
你太复杂了.我怀疑的第一个问题是你(或你的业务分析师)对业务主题没有足够的把握 - 即转移.你在这里有两个不同的枚举:
public enum ScheduleType
{
Unknown = 0,
Fixed,
Rotated
}
public enum ScheduleLoad
{
Unknown = 0,
FullTime,
PartTime
}
Run Code Online (Sandbox Code Playgroud)
接下来,在UI中,您需要两个不同的下拉框/单选按钮组,以允许用户排列移位布局,然后将其保存在对象的两个不同属性中.
但是,如果您坚持在一个枚举中使用它,因此一个带有标记枚举值的属性,则需要在将标志保存到存储之前验证用户输入.
[Flags]
public enum ShiftLayout
{
Unknown = 0,
Fixed = 1,
Rotated = 2,
FullTime = 4,
PartTime = 8,
Flexible = 16
}
Run Code Online (Sandbox Code Playgroud)
然后验证执行如下:
public bool IsShiftLayoutValid(ShiftLayout layout)
{
var isValid = layout.HasFlag(ShiftLayout.Flexible)
&& (layout & ~ShiftLayout.Flexible) == ShiftLayout.Unknown;
if (!isValid && !layout.HasFlag(ShiftLayout.Flexible))
{
var hasValidSchedule = (layout.HasFlag(ShiftLayout.Fixed) && !layout.HasFlag(ShiftLayout.Rotated))
|| layout.HasFlag(ShiftLayout.Rotated);
var hasValidTime = (layout.HasFlag(ShiftLayout.FullTime) && !layout.HasFlag(ShiftLayout.PartTime))
|| layout.HasFlag(ShiftLayout.PartTime);
isValid = hasValidSchedule && hasValidTime;
}
return isValid;
}
Run Code Online (Sandbox Code Playgroud)
以下ScheduleType示例能够保存与使用位字段类似的多种类型.请注意用于类型值的十六进制值,这些值允许逻辑操作确定构成当前值的类型.
public class ScheduleType : FlagsValueObject<ScheduleType> {
public static readonly ScheduleType Fixed = new ScheduleType(0x01, "Fixed");
public static readonly ScheduleType Flexible = new ScheduleType(0x02, "Flexible");
public static readonly ScheduleType FullTime = new ScheduleType(0x04, "Full Time");
public static readonly ScheduleType PartTime = new ScheduleType(0x08, "Part Time");
public static readonly ScheduleType Rotated = new ScheduleType(0x10, "Rotated");
protected ScheduleType(int value, string name)
: base(value, name) {
}
private ScheduleType(ScheduleType a, ScheduleType b) {
foreach (var kvp in a.Types.Union(b.Types)) {
Types[kvp.Key] = kvp.Value;
}
Name = string.Join(", ", Types.OrderBy(kvp => kvp.Value).Select(kvp => kvp.Value)) + " Work Schedule";
Value = Types.Keys.Sum();
}
protected override ScheduleType Or(ScheduleType other) {
var result=new ScheduleType(this, other);
//Applying validation rules on new combination
if (result.HasFlag(Fixed) && result.HasFlag(Rotated))
throw new InvalidOperationException("ScheduleType cannot be both Fixed and Rotated");
if (result.HasFlag(FullTime) && result.HasFlag(PartTime))
throw new InvalidOperationException("ScheduleType cannot be both FullTime and PartTime");
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
使用HasFlag确定标志内存在的组合,可以应用所需的业务规则.
例如
//Applying validation rules on new combination
if (result.HasFlag(Fixed) && result.HasFlag(Rotated))
throw new InvalidOperationException("ScheduleType cannot be both Fixed and Rotated");
if (result.HasFlag(FullTime) && result.HasFlag(PartTime))
throw new InvalidOperationException("ScheduleType cannot be both FullTime and PartTime");
Run Code Online (Sandbox Code Playgroud)
组合标志时应用这些规则以防止创建任何不需要的组合.
它源自以下支持值对象
public abstract class FlagsValueObject<T> : EnumValueObject where T : FlagsValueObject<T> {
protected readonly IDictionary<int, string> Types = new SortedDictionary<int, string>();
protected FlagsValueObject(int value, string name)
: base(value, name) {
Types[value] = name;
}
protected FlagsValueObject() {
}
public static T operator |(FlagsValueObject<T> left, T right) {
return left.Or(right);
}
protected abstract T Or(T other);
public virtual bool HasFlag(T flag) {
return flag != null && (Value & flag.Value) == flag.Value;
}
public virtual bool HasFlagValue(int value) {
return (Value & value) == value;
}
}
public class EnumValueObject : IEquatable<EnumValueObject>, IComparable<EnumValueObject> {
protected EnumValueObject(int value, string name) {
Value = value;
Name = name;
}
protected EnumValueObject() {
}
public virtual string Name { get; protected set; }
public virtual int Value { get; protected set; }
public static bool operator ==(EnumValueObject left, EnumValueObject right) {
return Equals(left, right);
}
public static bool operator !=(EnumValueObject left, EnumValueObject right) {
return !Equals(left, right);
}
public int CompareTo(EnumValueObject other) {
return Value.CompareTo(other.Value);
}
public bool Equals(EnumValueObject other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Value.Equals(other.Value);
}
public override bool Equals(object obj) {
return obj is EnumValueObject && Equals((EnumValueObject)obj);
}
public override int GetHashCode() {
return Value.GetHashCode();
}
public override string ToString() {
return Name;
}
}
Run Code Online (Sandbox Code Playgroud)
简单的单元测试示例.
[TestClass]
public class ScheduleTypeValueObjectTests {
[TestMethod]
public void Should_Merge_Names() {
//Arrange
var fixedSchedult = ScheduleType.Fixed; //Fixed Work Schedule
var fullTime = ScheduleType.FullTime; // Full Time Work Schedule
var type = fixedSchedult | fullTime;
//Act
var actual = type.Name;
//Assert
actual.Should().Be("Fixed, Full Time Work Schedule");
}
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void Should_Fail_Bitwise_Combination() {
//Arrange
var fullTime = ScheduleType.FullTime; // Full Time Work Schedule
var partTime = ScheduleType.PartTime;
var value = fullTime | partTime;
}
}
Run Code Online (Sandbox Code Playgroud)
该HasFlag属性允许检查标志中存在哪些类型,如以下示例所示.
public class Schedule {
public Schedule(
//...
ScheduleType scheduleType
//...
) {
//...
ScheduleType = scheduleType;
}
//...
public ScheduleType ScheduleType { get; set; }
public bool IsFixed {
get {
return ScheduleType != null && ScheduleType.HasFlag(ScheduleType.Fixed);
}
}
public bool IsFlexible {
get {
return
ScheduleType != null && ScheduleType.HasFlag(ScheduleType.Flexible);
}
}
public bool IsFullTime {
get {
return
ScheduleType != null && ScheduleType.HasFlag(ScheduleType.FullTime);
}
}
//...
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
430 次 |
| 最近记录: |