C# 如何在解耦对象之间实施约束?
注意-我将原来的帖子移到了底部,因为我认为它对这个帖子的新手仍然有价值。下面是根据反馈改写问题的尝试 完全编辑的帖子 好的,我将试着详细阐述一下我的具体问题。我意识到我将域逻辑与接口/表示逻辑混合了一点,但老实说,我不确定在哪里将其分开。请容忍我:) 我正在编写一个应用程序,该应用程序(除其他外)执行物流模拟以移动物品。其基本思想是,用户可以看到一个类似于Visual Studio的项目,在该项目中,她可以添加、删除、命名、组织、注释等我将要概述的各种对象:C# 如何在解耦对象之间实施约束?,c#,events,decoupling,intercept,C#,Events,Decoupling,Intercept,注意-我将原来的帖子移到了底部,因为我认为它对这个帖子的新手仍然有价值。下面是根据反馈改写问题的尝试 完全编辑的帖子 好的,我将试着详细阐述一下我的具体问题。我意识到我将域逻辑与接口/表示逻辑混合了一点,但老实说,我不确定在哪里将其分开。请容忍我:) 我正在编写一个应用程序,该应用程序(除其他外)执行物流模拟以移动物品。其基本思想是,用户可以看到一个类似于Visual Studio的项目,在该项目中,她可以添加、删除、命名、组织、注释等我将要概述的各种对象: 项和位置是基本的无行为数据项 cl
- 项和位置是基本的无行为数据项
class Item { ... } class Location { ... }
- WorldState是项目位置对的集合。WorldState是可变的:用户可以添加和删除项目,或更改其位置
class WorldState : ICollection<Tuple<Item,Location>> { }
var bicycle = new Item();
var surfboard = new Item();
var football = new Item();
var hat = new Item();
var myHouse = new Location();
var theBeach = new Location();
var thePark = new Location();
var stuffAtMyHouse = new WorldState( new Dictionary<Item, Location>() {
{ hat, myHouse },
{ bicycle, myHouse },
{ surfboard, myHouse },
{ football, myHouse },
};
var gotoTheBeach = new Plan(StartState: stuffAtMyHouse , Plan : new [] {
new [] { surfboard, theBeach, 1/1/2010 10AM }, // go surfing
new [] { surfboard, myHouse, 1/1/2010 5PM }, // come home
});
var gotoThePark = new Plan(StartState: stuffAtMyHouse , Plan : new [] {
new [] { football, thePark, 1/1/2010 10AM }, // play footy in the park
new [] { football, myHouse, 1/1/2010 5PM }, // come home
});
var bigDayOut = new Plan(StartState: stuffAtMyHouse , Plan : new [] {
new [] { bicycle, theBeach, 1/1/2010 10AM }, // cycle to the beach to go surfing
new [] { surfboard, theBeach, 1/1/2010 10AM },
new [] { bicycle, thePark, 1/1/2010 1PM }, // stop by park on way home
new [] { surfboard, thePark, 1/1/2010 1PM },
new [] { bicycle, myHouse, 1/1/2010 1PM }, // head home
new [] { surfboard, myHouse, 1/1/2010 1PM },
});
var s1 = new Simulation(...);
var s2 = new Simulation(...);
var s3 = new Simulation(...);
IEnumerable<SimulationResult> results =
from simulation in new[] {s1, s2}
from plan in new[] {gotoTheBeach, gotoThePark, bigDayOut}
select simulation.Execute(plan);
因此,基本上,当用户试图通过world.removietem(item)
调用从WorldState(可能是整个项目)中删除一个项时,我希望确保在使用该WorldState的任何计划对象中都不会引用该项。如果是,我想告诉用户“嘿!下面的Plan X正在使用此项!在尝试删除它之前,先处理它!”。我确实不想从世界中得到的那种行为。RemoveItem(item)
调用是:
- 正在删除该项,但仍有计划引用该项
- 删除项目,但让计划以静默方式删除其列表中引用该项目的所有元素。(事实上,这可能是可取的,但只是作为第二选择)
world.removietem(项目)
自己,以及(b)我不相信这种行为是纯粹的“用户界面”问题
呸。我希望有人还在读
原创帖子
假设我有以下几个类:
public class Starport
{
public string Name { get; set; }
public double MaximumShipSize { get; set; }
}
public class Spaceship
{
public readonly double Size;
public Starport Home;
}
因此,假设存在一个约束条件,即宇宙飞船的大小必须小于或等于其所在地的最大飞船大小
那么我们该如何应对呢
传统上我做过这样的事情:
partial class Starport
{
public HashSet<Spaceship> ShipsCallingMeHome; // assume this gets maintained properly
private double _maximumShipSize;
public double MaximumShipSize
{
get { return _maximumShipSize; }
set
{
if (value == _maximumShipSize) return;
foreach (var ship in ShipsCallingMeHome)
if (value > ship)
throw new ArgumentException();
_maximumShipSize = value
}
}
}
interface ISpacebaseInterceptor<T>
{
bool RequestChange(T newValue);
void NotifyChange(T newValue);
}
但我不确定这是否更好。我也不确定以这种方式滚动我自己的事件是否会对性能产生某些影响,或者这可能是一个好主意/坏主意的其他原因
}你知道宇宙飞船必须有一定的尺寸;将大小放在基类中,并在那里的访问器中实现验证检查
我知道这似乎过于关注您的具体实现,但这里的要点是您的期望并不像您期望的那样解耦;如果您在基类中对派生类中的某些内容有一个严格的期望,那么您的基类正在对提供该类实现的派生类做出一个基本的期望;也可以直接将该期望迁移到基类,在这里你可以更好地管理约束。 < P>你可以做一些像C++ STL特性类的事情——实现一个通用的<代码> SpaceBase < /C> >,它有两个参数化<代码>类型< /Calp>S-一个,定义了<代码>飞船/<代码>成员,另一个约束
SpaceBase
及其SpaceShip
的方法是使用SpaceBaseTraits
类来封装基地的特性,例如它可以包含的对飞船的限制。InotifyPropertyChange接口是为数据绑定而设计的,这就解释了为什么它没有你想要的能力。我可以试试这样的东西:
partial class Starport
{
public HashSet<Spaceship> ShipsCallingMeHome; // assume this gets maintained properly
private double _maximumShipSize;
public double MaximumShipSize
{
get { return _maximumShipSize; }
set
{
if (value == _maximumShipSize) return;
foreach (var ship in ShipsCallingMeHome)
if (value > ship)
throw new ArgumentException();
_maximumShipSize = value
}
}
}
interface ISpacebaseInterceptor<T>
{
bool RequestChange(T newValue);
void NotifyChange(T newValue);
}
接口ISpacebaseInterceptor
{
bool-RequestChange(T-newValue);
变更无效(T新值);
}
根据修订后的问题:
我认为WorldState
类需要一个委托。。。而Plan
将设置一个方法,该方法应被调用以测试项目是否正在使用。诸如此类:
delegate bool IsUsedDelegate(Item Item);
public class WorldState {
public IsUsedDelegate CheckIsUsed;
public bool RemoveItem(Item item) {
if (CheckIsUsed != null) {
foreach (IsUsedDelegate checkDelegate in CheckIsUsed.GetInvocationList()) {
if (checkDelegate(item)) {
return false; // or throw exception
}
}
}
// Remove the item
return true;
}
}
然后,在计划的构造函数中,设置要调用的委托
public class plan {
public plan(WorldState state) {
state.IsUsedDelegate += CheckForItemUse;
}
public bool CheckForItemUse(Item item) {
// Am I using it?
}
}
这是非常粗糙的,当然,我会试着在午餐后补充更多:)但是你明白了
(午餐后:)
缺点是您必须依靠计划来设置代理。。。但这是无法避免的。一个项目
无法告诉它有多少引用,或者
delegate bool IsUsedDelegate(Item Item);
public class WorldState {
public IsUsedDelegate CheckIsUsed;
public bool RemoveItem(Item item) {
if (CheckIsUsed != null) {
foreach (IsUsedDelegate checkDelegate in CheckIsUsed.GetInvocationList()) {
if (checkDelegate(item)) {
return false; // or throw exception
}
}
}
// Remove the item
return true;
}
}
public class plan {
public plan(WorldState state) {
state.IsUsedDelegate += CheckForItemUse;
}
public bool CheckForItemUse(Item item) {
// Am I using it?
}
}
public class Starport
{
public string Name { get; protected set; }
public double MaximumShipSize { get; protected set; }
public AircarfDispatcher GetDispatcherOnDuty() {
return new AircarfDispatcher(this); // It can be decoupled further, just example
}
}
public class Spaceship
{
public double Size { get; private set; };
public Starport Home {get; protected set;};
}
public class AircarfDispatcher
{
Startport readonly airBase;
public AircarfDispatcher(Starport airBase) { this.airBase = airBase; }
public bool CanLand(Spaceship ship) {
if (ship.Size > airBase.MaximumShipSize)
return false;
return true;
}
public bool CanTakeOff(Spaceship ship) {
return true;
}
public bool Land(Spaceship ship) {
var canLand = CanLand(ship);
if (!canLand)
throw new ShipLandingException(airBase, this, ship, "Not allowed to land");
// Do something with the capacity of Starport
}
}
// Try to land my ship to the first available port
var ports = GetPorts();
var onDuty = ports.Select(p => p.GetDispatcherOnDuty())
.Where(d => d.CanLand(myShip)).First();
onDuty.Land(myShip);
// try to resize! But NO we cannot do that (setter is protected)
// because it is not the responsibility of the Port, but a building company :)
ports.First().MaximumShipSize = ports.First().MaximumShipSize / 2.0