C# 如何最好地设计可伸缩类?
我的意思是: 我现在基本上有一个类,它有太多的属性和函数。为了保持性能和可理解性,它需要以某种方式缩小。但我仍然需要所有这些属性和方法。 现在是这样的:C# 如何最好地设计可伸缩类?,c#,architecture,class-design,C#,Architecture,Class Design,我的意思是: 我现在基本上有一个类,它有太多的属性和函数。为了保持性能和可理解性,它需要以某种方式缩小。但我仍然需要所有这些属性和方法。 现在是这样的: class Apple float seedCount; ... ...about 25 variables and properties here. void Update() <-- a huge method that checks for each property and updates if so 因此产生
class Apple
float seedCount;
...
...about 25 variables and properties here.
void Update() <-- a huge method that checks for each property and updates if so
因此产生了许多问题:
我必须经常检查每一个物体和每一帧的特征。如果种子没有初始化,我不必为它计算任何东西。如果是的话,我必须这么做。
如果我决定在Seed类中放入多个属性/特性,我还需要检查其中的每一个。
事情变得越来越复杂了。因此,我的问题是,我需要对所有特性进行粒度控制,无法将它们智能地划分为更大的子类。任何形式的子类都只包含一组需要检查和更新的属性。
我不能准确地创建Apple的子类,因为需要这样高粒度的控件。创建尽可能多的属性组合的类是疯狂的。
我的主要目标是:我想要简短的代码
我的主要目标是:我想要简短的代码
选项:
Update
方法,并对其各个部分进行必要的计算,然后调用base.Update
将其传递给下一行
您的最终答案将在很大程度上取决于您的“苹果”到底是什么。您可以在Apple类中使用嵌套类
我认为这里的关键是你要把所有的东西都放在一节课上。因此,类必须不断地检查它有什么和没有什么。解决方案是创建子类或装饰器,这些子类或装饰器已经知道它们是否有特定的东西。这样他们就不必每次都检查了
因为你有这么多的属性,它们可能以不同的方式组合在一起,所以听起来装饰解决方案更适合你。我认为你走的是正确的道路:组合。您可以将您的类与所需的其他类组合在一起。但你也需要相应地委派责任。在您的示例中,应该由
Seed
类负责检查其内部状态,而Apple
只是委托给它
至于可选特性问题,也许您可以使用而不是空引用。这样,您就不必每次都检查
null
,代码就更加一致了。如果您的方法应该是空的,那么您的方法可能不是空的
如果将Seed类与Apple类分开,为什么不将使用Seed信息的方法也移动到Seed类中呢
如果这些方法需要有关其他Apple属性的信息,您可以将其作为参数传递
通过这样做,我想您可以消除初始化检查
这是一本关于如何解决此类问题的好书:
这个问题我已经思考了一段时间,我想出了另一个解决方案。这可能有点离经叛道和反面向对象,但如果你还不灰心,请继续阅读 以Apple示例为基础:Apple类可以包含许多属性,这些属性可以归类为相关组。例如,我使用了一个Apple类,该类的一些属性与苹果的种子有关,而其他属性与苹果的皮肤有关
A.种子
a1。GetSeedCount
a2
B皮肤
b1。GetSkinColor
b2李> 我正在使用dictionary对象存储所有Apple属性 我编写了扩展方法来定义属性的访问器,使用不同的类将它们分开并组织起来 通过对属性使用字典,您可以在任何时候迭代存储在thusfar中的所有属性(如果您必须检查所有属性,因为这听起来像是您在更新方法中需要的)。不幸的是,您丢失了数据的强类型(至少在我的示例中是这样,因为我使用的是字典
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();
}
}
}
这是一次试驾:
Program.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";
}
}
}
}
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());
}
}
}
在11月7日(日期,而不是商店)发表评论:)在您提供的示例代码中,有一条注释说明: “如你所见,我不能打电话 关于“怪物”的其他基本方法 你意识到你可以在这一点上做类似的事情:
BasicBroodmother bm = monster as BasicBroodmother;
if (bm != null)
{
bm.Eat();
}
您的代码没有太多内容(我知道这只是一个示例),但当我看到它时,我觉得您应该能够改进设计
BasicBroodmother bm = monster as BasicBroodmother;
if (bm != null)
{
bm.Eat();
}