我一直在思考这个问题,并想出了一个替代解决方案。这可能有点不正统和反对象导向,但如果你不是胆小,请继续阅读......
以 Apple 示例为基础:Apple 类可以包含许多属性,这些属性可以分为相关组。例如,我使用了一个 Apple 类,其中一些属性与苹果种子相关,而其他属性与苹果皮相关。
- 苹果
一种。种子
1。获取种子计数
a2。 ...
湾。皮肤
b1。获取皮肤颜色
b2。 ...
我正在使用字典对象来存储所有苹果属性。
我编写了扩展方法来定义属性的访问器,使用不同的类来保持它们的分离和组织。
通过对属性使用字典,您可以在任何时候遍历存储的所有属性(如果您必须检查所有属性,因为这听起来像是您在更新方法中需要的那样)。不幸的是,您丢失了数据的强类型(至少在我的示例中我这样做了,因为我使用的是 Dictionary。您可以为所需的每种类型使用单独的字典,但这需要更多的管道代码来路由属性访问正确的字典。
使用扩展方法来定义属性的访问器允许您为每个逻辑类别的属性分离代码。这样可以将事物组织成独立的相关逻辑块。
这是我想出的一个示例,用于测试这将如何工作,并给出了标准警告,即如果您要继续沿此路径进行稳健化(验证、错误处理等)。
Apple.cs
namespace ConsoleApplication1
{
using System.Collections.Generic;
using System.Text;
public class Apple
{
// Define the set of valid properties for all apple objects.
private static HashSet<string> AllowedProperties = new HashSet<string>(
new string [] {
"Color",
"SeedCount"
});
// The main store for all properties
private Dictionary<string, string> Properties = new Dictionary<string, string>();
// Indexer for accessing properties
// Access via the indexer should be restricted to the extension methods!
// Unfortunately can't enforce this by making it private because then extension methods wouldn't be able to use it as they are now.
public string this[string prop]
{
get
{
if (!AllowedProperties.Contains(prop))
{
// throw exception
}
if (Properties.ContainsKey(prop))
{
return this.Properties[prop];
}
else
{
// TODO throw 'property unitialized' exeception || lookup & return default value for this property || etc.
// this return is here just to make the sample runable
return "0";
}
}
set
{
if (!AllowedProperties.Contains(prop))
{
// TODO throw 'invalid property' exception
// these assignments are here just to make the sample runable
prop = "INVALID";
value = "0";
}
this.Properties[prop] = value.ToString();
}
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach (var kv in this.Properties)
{
sb.AppendFormat("{0}={1}\n", kv.Key, kv.Value);
}
return sb.ToString();
}
}
}
AppleExtensions.cs
namespace AppleExtensionMethods
{
using System;
using ConsoleApplication1;
// Accessors for Seed Properties
public static class Seed
{
public static float GetSeedCount(this Apple apple)
{
return Convert.ToSingle(apple["SeedCount"]);
}
public static void SetSeedCount(this Apple apple, string count)
{
apple["SeedCount"] = count;
}
}
// Accessors for Skin Properties
public static class Skin
{
public static string GetSkinColor(this Apple apple)
{
return apple["Color"];
}
public static void SetSkinColor(this Apple apple, string color)
{
apple["Color"] = ValidSkinColorOrDefault(apple, color);
}
private static string ValidSkinColorOrDefault(this Apple apple, string color)
{
switch (color.ToLower())
{
case "red":
return color;
case "green":
return color;
default:
return "rotten brown";
}
}
}
}
这是试驾:
Program.cs
namespace ConsoleApplication1
{
using System;
using AppleExtensionMethods;
class Program
{
static void Main(string[] args)
{
Apple apple = new Apple();
apple.SetSkinColor("Red");
apple.SetSeedCount("8");
Console.WriteLine("My apple is {0} and has {1} seed(s)\r\n", apple.GetSkinColor(), apple.GetSeedCount());
apple.SetSkinColor("green");
apple.SetSeedCount("4");
Console.WriteLine("Now my apple is {0} and has {1} seed(s)\r\n", apple.GetSkinColor(), apple.GetSeedCount());
apple.SetSkinColor("blue");
apple.SetSeedCount("0");
Console.WriteLine("Now my apple is {0} and has {1} seed(s)\r\n", apple.GetSkinColor(), apple.GetSeedCount());
apple.SetSkinColor("yellow");
apple.SetSeedCount("15");
Console.WriteLine(apple.ToString());
// Unfortunatly there is nothing stopping users of the class from doing something like that shown below.
// This would be bad because it bypasses any behavior that you have defined in the get/set functions defined
// as extension methods.
// One thing in your favor here is it is inconvenient for user of the class to find the valid property names as
// they'd have to go look at the apple class. It's much easier (from a lazy programmer standpoint) to use the
// extension methods as they show up in intellisense :) However, relying on lazy programming does not a contract make.
// There would have to be an agreed upon contract at the user of the class level that states,
// "I will never use the indexer and always use the extension methods!"
apple["Color"] = "don't panic";
apple["SeedCount"] = "on second thought...";
Console.WriteLine(apple.ToString());
}
}
}
从 7 月 11 日开始处理您的评论(日期,而不是商店):)
在您提供的示例代码中,有一条注释指出:
“如你所见,我不能打电话
“怪物”上的基本育母方法
你意识到你可以在那个时候做这样的事情:
BasicBroodmother bm = monster as BasicBroodmother;
if (bm != null)
{
bm.Eat();
}
您的代码没有太多内容,(我知道这只是一个示例),但是当我查看它时,我觉得您应该能够改进设计。我的直接想法是为 broodmother 提供一个抽象类,其中包含所有 broodmother 共有的任何属性/动作的默认实现。然后,专门的育母,如魔法育母,将包含任何特定于魔法育母的专门属性/动作,但也继承自抽象类,并在必要时覆盖必要的基本属性/动作。
我会看一下用于设计动作的策略模式,以便可以根据怪物的类型交换动作(即吃、产卵、攻击等行为)。
[编辑 7/13]
现在没有时间详细介绍(需要睡觉),但我整理了一些 sample code 展示了不同的方法。
代码组成:
- Broodfather.cs - 抽象类,其中包含不同 Broodfathers“类型”共有的所有内容。
- BasicBroodFather.cs - 从 Broodfather 继承的具体类。
- BroodfatherDecorator.cs - 由所有 Broodfather 装饰器继承的抽象类。
- MagicalBroodfather.cs - 这个类用“魔法”装饰/包装一个育父
- BloodthirstyBroodfather.cs - 这个类用“bloodthirst”装饰/包装一个育父
- program.cs - 演示了两个示例:第一个示例从一个被魔法包裹的基本育父开始,然后被嗜血包裹。第二个从一个基本的育父开始,然后将其包裹在另一个顺序嗜血,然后是魔法。