完全删除的帖子
好的,我会尽量详细说明我的具体问题。我意识到我有点混淆了域逻辑和界面/表现逻辑,但老实说我不知道该如何分离它们。请耐心等待 :)
我正在编写一个应用程序,其中(除其他事项外)执行物流模拟以移动物品。基本思路是用户看到一个项目,类似于Visual Studio,在其中可以添加、删除、命名、组织、注释等各种对象,我将要概述这些对象:
Items and Locations are basic behaviourless data items.
class Item { ... } class Location { ... }
A WorldState is a Collection of item-location pairs. A WorldState is mutable: The user is able to add and remove items, or change their location.
class WorldState : ICollection<Tuple<Item,Location>> { }
A Plan represents the movement of items to different locations at desired times. These can either be imported into the Project or generated within the program. It references a WorldState to get the initial location of various objects. A Plan is also mutable.
class Plan : IList<Tuple<Item,Location,DateTime>> { WorldState StartState { get; } }
A Simulation then executes a Plan. It encapsulates a lot of rather complex behaviour, and other objects, but the end result is a SimulationResult which is a set of metrics that basically describe how much this cost and how well the Plan was fulfilled (think the Project Triangle)
class Simulation { public SimulationResult Execute(Plan plan); } class SimulationResult { public Plan Plan { get; } }
冒着过于冗长的风险,以下是一个示例。
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);
问题在于执行类似以下代码时:
stuffAtMyHouse.RemoveItem(hat); // this is fine
stuffAtMyHouse.RemoveItem(bicycle); // BAD! bicycle is used in bigDayOut,
基本上,当用户尝试通过world.RemoveItem(item)
调用从WorldState(也许是整个项目)中删除一个项目时,我希望确保该项目不在任何使用该WorldState的Plan对象中被引用。如果是这样,我想告诉用户“嘿!以下计划X正在使用此项!在尝试删除它之前去处理一下它!”我不希望从world.RemoveItem(item)
调用中出现以下行为:
- 删除该项目但仍然让计划引用它。
- 删除该项目但使计划默默地删除其列表中所有引用该项目的元素。(实际上,这可能是可取的,但只作为次要选项。)
哇,希望有人还在阅读......
原帖标题:假设我有以下类:
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
}
}
}
这在简单的示例中是可以管理的(因此可能是一个糟糕的示例),但我发现随着约束变得越来越大且更加复杂,以及我希望有更多相关功能(例如实现一个方法
bool CanChangeMaximumShipSizeTo(double)
或其他收集太大的船只的方法),我最终编写了更多不必要的双向关系(在这种情况下,SpaceBase-Spaceship被认为是适当的),并且编写了复杂的代码,它在所有者方程式中基本上是无关紧要的。那么,这种情况通常如何处理? 我考虑过以下几点:
我考虑使用事件,类似于ComponentModel INotifyPropertyChanging / PropertyChanging模式,但EventArgs将具有某种Veto()或Error()功能(就像winforms允许您使用密钥或禁止表单退出一样)。 但是我不确定这是否构成了事件滥用。
或者,通过显式定义的接口自己管理事件,例如
asdf我需要在这里插入此行,否则格式不正确。
interface IStarportInterceptor
{
bool RequestChangeMaximumShipSize(double newValue);
void NotifyChangeMaximumShipSize(double newValue);
}
partial class Starport
{
public HashSet<ISpacebaseInterceptor> interceptors; // assume this gets maintained properly
private double _maximumShipSize;
public double MaximumShipSize
{
get { return _maximumShipSize; }
set
{
if (value == _maximumShipSize) return;
foreach (var interceptor in interceptors)
if (!RequestChangeMaximumShipSize(value))
throw new ArgumentException();
_maximumShipSize = value;
foreach (var interceptor in interceptors)
NotifyChangeMaximumShipSize(value);
}
}
}
但我不确定这样是否更好。我也不确定用这种方式自己编写事件是否会有某些性能影响,或者是否有其他原因可以解决这个问题。
第三种选择可能是使用PostSharp或IoC/依赖注入容器进行非常古怪的AOP。但我还没有准备好走这条路。
上帝对象管理所有检查等-仅在stackoverflow搜索god object时,我就觉得这是错误和不好的
我的主要关注点是这似乎是一个相当明显的问题,并且我认为这应该是一个相当常见的问题,但我没有看到任何讨论(例如System.ComponentModel没有提供任何设施来否决PropertyChanging事件-对吗?);这让我担心我(再次)未能掌握耦合或(更糟糕的是)面向对象设计的一些基本概念。
评论?