具有 class 的工厂模式可以具有不同的 class 子类型
Factory pattern with a class that can has different class sub types
我有来自多个组织(警察、消防、办公室)的数据,需要以不同格式输出。
为了实现这一点,我定义了以下内容(这有点简化):
T运行行动class -
- "Success" 指标 - 布尔值。
- "Type of department"- 字符串或枚举。
- A class 可以是任何类型 - 警察、消防或办公室(如您所见,我的问题是关于此的)。
- GenerateOutput() 方法 - 处理文件格式的生成。
警察class
- 年龄 - 字符串
- VehicleNumber - 整数
- 主管 - 字符串
火 class
- 名称 - 字符串
- FireEngineNumber - 整数
- 县 - 枚举
- WorkTimings - 枚举
办公室Class
- 年龄 - 字符串
- DeskNumber - 整数
- 部门 - 字符串
- PayScale - 枚举
- IsManagement - 布尔值
如您所见,警察、消防和办公室 classes 没有任何共同点,主要用作数据承载实体。我打算使用工厂来 return 一个适当的泛型(不是 C# 泛型)T运行saction 对象与数据(T运行saction 对象与 Police, Fire或其中的 Office 数据),然后将 returned 对象传递给确定文件格式的策略模式(CSV、Excel 或 XML;在配置文件)每个都需要。
我的问题出在 T运行saction 对象的定义上。
“3”中的class是什么类型。需要 T运行saction class 吗?每个组织的数据不同,没有共同的成员,我无法为所有人定义一个共同的class。
整体设计是否合适?我还应该考虑哪些其他设计?
根据彼得的以下评论:
我认为使用泛型可能有效,但我 运行 遇到了问题。我想使用工厂 return 请求的对象,使用 GetT运行sactionObject,如下所示。 return 类型的 GetT运行sactionObject 应该是什么来适应这个。
class T运行sactionFactory
{
Dictionary<string, Type> typeClassLookup;
public TransactionFactory()
{
typeClassLookup = new Dictionary<string, Type>();
typeClassLookup.Add("Police", typeof(PoliceData));
typeClassLookup.Add("Fire", typeof(FireData));
}
Transaction<????> GetTransactionObject(string org)
{
if( typeClassLookup.TryGetValue(org, out typeValue))
{
switch (typeValue.ToString())
{
case "policeData":
transactionObject = new Transaction<PoliceData>() { Data = new PoliceData(), params = null};
case "FireData":
transactionObject = new Transaction<FireData>() {Data = new FireData(), params = null};
}
}
return transactionObject;
如果类型真的没有任何共同点,那么您不需要显式基class。 System.Object
就足够了,就像许多其他泛型类型一样(即任何没有约束的泛型类型)。
换句话说,您可以声明为:
class Transaction<T>
{
public bool Success { get; private set; }
public T Entity { get; private set; }
public Transaction(bool success, T entity)
{
Success = success;
Entity = entity;
}
public void GenerateOutput() { /* something goes here */ }
}
就个人而言,我会避免添加 "department type" 成员。毕竟,这是类型参数 T
中隐含的。但是如果你愿意,你可以很容易地将它添加到上面。
如果您发现这些类型确实有一些共同点,那么您的 Transaction<T>
类型需要做的不仅仅是简单地保留其中一种类型的实例(这几乎是它所能做的)做没有约束),那么您将能够将该通用性放入接口或基础 class (取决于具体需要),并在 Transaction<T>
[=85= 的约束中指定].
请注意,您不清楚 GenerateOutput()
的意思,或者它应该如何工作。但是假设您想要针对每个 Entity
值的特定输出,在我看来 that 就是您的 "something in common"。即,根本不需要 Transaction<T>
class 来实现该方法,而是每个实体类型。在那种情况下,你有这样的东西:
interface IDepartmentEntity
{
void GenerateOutput();
}
class Office : IDepartmentEntity
{
public void GenerateOutput() { /* department-specific logic here */ }
}
// etc.
然后你可以声明:
class Transaction<T> where T : IDepartmentEntity
{
public bool Success { get; private set; }
public T Entity { get; private set; }
public Transaction(bool success, T entity)
{
Success = success;
Entity = entity;
}
public void GenerateOutput() { Entity.GenerateOutput(); }
}
编辑:
根据 Prasant 的后续编辑,请求对 GetTransactionObject()
…
提出建议
执行此操作的正确方法取决于调用者和上下文,问题中未提供详细信息。恕我直言,best 场景是调用者知道类型的地方。这允许使用泛型的全部功能。
例如:
class TransactionFactory
{
public Transaction<T> GetTransactionObject<T>()
where T : IDepartmentEntity, new()
{
return new Transaction<T>()
{
Data = new T(),
params = null
}
}
}
然后你这样调用:
Transaction<FireData> transaction = factory.GetTransactionObject<FireData>();
调用者当然已经知道它正在创建的类型,然后可以填写 transaction.Data
对象的适当属性。
如果该方法不可行,那么您将需要 Transaction<T>
本身有一个基础 class,或实现一个接口。注意,在我原来的例子中,IDepartmentEntity
接口只有一个方法,和Transaction
class.
接口中的GenerateOutput()
方法是一样的
也许,该接口实际上是关于生成输出而不是数据实体。调用它,而不是 IDepartmentEntity
,类似于 IOutputGenerator
.
在那种情况下,你可能会遇到这样的事情:
class Transaction<T> : IOutputGenerator
{
// all as before
}
class TransactionFactory
{
public IOutputGenerator GetTransactionObject(string org)
{
if( typeClassLookup.TryGetValue(org, out typeValue))
{
switch (typeValue.ToString())
{
case "policeData":
transactionObject = new Transaction<PoliceData>() { Data = new PoliceData(), params = null};
case "FireData":
transactionObject = new Transaction<FireData>() {Data = new FireData(), params = null};
}
}
return transactionObject;
}
}
这是一个较差的解决方案,因为这意味着调用者只能直接访问 IOutputGenerator
功能。其他任何事情都需要进行一些类型检查和特殊情况代码,真正应该尽可能避免的事情。
注意:如果 Transaction
类型有其他成员,像 GenerateOutput()
方法一样,独立于此处包含的类型 T
,这对调用者有用不知道 T
,那么上述的一个可能变体是不重用用于部门特定数据类型的接口,而是为 Transaction<T>
声明一个基础 class,命名为当然 Transaction
,包含与 T
无关的所有成员。那么return值可以是Transaction
.
- What type does the class in "3." of the Transaction class need to be?
要将您的部门 class 与各种导出类型分离,我建议您让部门 class 实现一个通用接口。像这样:
public interface Exportable {
// return a list of attribute names, values, and types to export
IList<Tuple<String, String, Type>> GetAttributes();
}
例如:
public class Police : Exportable {
public IList<Tuple<String, String, Type>> GetAttributes() {
// return list size 3 - attribute info for Age, VehicleNumber, Supervisor
}
}
- Is the overall design appropriate? What other designs should I consider?
Transaction
class 设计似乎不太适合这个问题。
考虑一个 Export
class,每个导出类型都有一个方法,每个方法接收从 Exportable
接口方法返回的属性。基本大纲:
public static class Export {
public static boolean CSV(IList<Tuple<String, String, Type>> attributes) {
// export attributes to CSV, return whether succeeded
}
public static boolean Excel(IList<Tuple<String, String, Type>> attributes) {
// export attributes to Excel, return whether succeeded
}
// same thing for XML
}
我有来自多个组织(警察、消防、办公室)的数据,需要以不同格式输出。
为了实现这一点,我定义了以下内容(这有点简化):
T运行行动class -
- "Success" 指标 - 布尔值。
- "Type of department"- 字符串或枚举。
- A class 可以是任何类型 - 警察、消防或办公室(如您所见,我的问题是关于此的)。
- GenerateOutput() 方法 - 处理文件格式的生成。
警察class
- 年龄 - 字符串
- VehicleNumber - 整数
- 主管 - 字符串
火 class
- 名称 - 字符串
- FireEngineNumber - 整数
- 县 - 枚举
- WorkTimings - 枚举
办公室Class
- 年龄 - 字符串
- DeskNumber - 整数
- 部门 - 字符串
- PayScale - 枚举
- IsManagement - 布尔值
如您所见,警察、消防和办公室 classes 没有任何共同点,主要用作数据承载实体。我打算使用工厂来 return 一个适当的泛型(不是 C# 泛型)T运行saction 对象与数据(T运行saction 对象与 Police, Fire或其中的 Office 数据),然后将 returned 对象传递给确定文件格式的策略模式(CSV、Excel 或 XML;在配置文件)每个都需要。
我的问题出在 T运行saction 对象的定义上。
“3”中的class是什么类型。需要 T运行saction class 吗?每个组织的数据不同,没有共同的成员,我无法为所有人定义一个共同的class。
整体设计是否合适?我还应该考虑哪些其他设计?
根据彼得的以下评论: 我认为使用泛型可能有效,但我 运行 遇到了问题。我想使用工厂 return 请求的对象,使用 GetT运行sactionObject,如下所示。 return 类型的 GetT运行sactionObject 应该是什么来适应这个。
class T运行sactionFactory {
Dictionary<string, Type> typeClassLookup;
public TransactionFactory()
{
typeClassLookup = new Dictionary<string, Type>();
typeClassLookup.Add("Police", typeof(PoliceData));
typeClassLookup.Add("Fire", typeof(FireData));
}
Transaction<????> GetTransactionObject(string org)
{
if( typeClassLookup.TryGetValue(org, out typeValue))
{
switch (typeValue.ToString())
{
case "policeData":
transactionObject = new Transaction<PoliceData>() { Data = new PoliceData(), params = null};
case "FireData":
transactionObject = new Transaction<FireData>() {Data = new FireData(), params = null};
}
}
return transactionObject;
如果类型真的没有任何共同点,那么您不需要显式基class。 System.Object
就足够了,就像许多其他泛型类型一样(即任何没有约束的泛型类型)。
换句话说,您可以声明为:
class Transaction<T>
{
public bool Success { get; private set; }
public T Entity { get; private set; }
public Transaction(bool success, T entity)
{
Success = success;
Entity = entity;
}
public void GenerateOutput() { /* something goes here */ }
}
就个人而言,我会避免添加 "department type" 成员。毕竟,这是类型参数 T
中隐含的。但是如果你愿意,你可以很容易地将它添加到上面。
如果您发现这些类型确实有一些共同点,那么您的 Transaction<T>
类型需要做的不仅仅是简单地保留其中一种类型的实例(这几乎是它所能做的)做没有约束),那么您将能够将该通用性放入接口或基础 class (取决于具体需要),并在 Transaction<T>
[=85= 的约束中指定].
请注意,您不清楚 GenerateOutput()
的意思,或者它应该如何工作。但是假设您想要针对每个 Entity
值的特定输出,在我看来 that 就是您的 "something in common"。即,根本不需要 Transaction<T>
class 来实现该方法,而是每个实体类型。在那种情况下,你有这样的东西:
interface IDepartmentEntity
{
void GenerateOutput();
}
class Office : IDepartmentEntity
{
public void GenerateOutput() { /* department-specific logic here */ }
}
// etc.
然后你可以声明:
class Transaction<T> where T : IDepartmentEntity
{
public bool Success { get; private set; }
public T Entity { get; private set; }
public Transaction(bool success, T entity)
{
Success = success;
Entity = entity;
}
public void GenerateOutput() { Entity.GenerateOutput(); }
}
编辑:
根据 Prasant 的后续编辑,请求对 GetTransactionObject()
…
执行此操作的正确方法取决于调用者和上下文,问题中未提供详细信息。恕我直言,best 场景是调用者知道类型的地方。这允许使用泛型的全部功能。
例如:
class TransactionFactory
{
public Transaction<T> GetTransactionObject<T>()
where T : IDepartmentEntity, new()
{
return new Transaction<T>()
{
Data = new T(),
params = null
}
}
}
然后你这样调用:
Transaction<FireData> transaction = factory.GetTransactionObject<FireData>();
调用者当然已经知道它正在创建的类型,然后可以填写 transaction.Data
对象的适当属性。
如果该方法不可行,那么您将需要 Transaction<T>
本身有一个基础 class,或实现一个接口。注意,在我原来的例子中,IDepartmentEntity
接口只有一个方法,和Transaction
class.
GenerateOutput()
方法是一样的
也许,该接口实际上是关于生成输出而不是数据实体。调用它,而不是 IDepartmentEntity
,类似于 IOutputGenerator
.
在那种情况下,你可能会遇到这样的事情:
class Transaction<T> : IOutputGenerator
{
// all as before
}
class TransactionFactory
{
public IOutputGenerator GetTransactionObject(string org)
{
if( typeClassLookup.TryGetValue(org, out typeValue))
{
switch (typeValue.ToString())
{
case "policeData":
transactionObject = new Transaction<PoliceData>() { Data = new PoliceData(), params = null};
case "FireData":
transactionObject = new Transaction<FireData>() {Data = new FireData(), params = null};
}
}
return transactionObject;
}
}
这是一个较差的解决方案,因为这意味着调用者只能直接访问 IOutputGenerator
功能。其他任何事情都需要进行一些类型检查和特殊情况代码,真正应该尽可能避免的事情。
注意:如果 Transaction
类型有其他成员,像 GenerateOutput()
方法一样,独立于此处包含的类型 T
,这对调用者有用不知道 T
,那么上述的一个可能变体是不重用用于部门特定数据类型的接口,而是为 Transaction<T>
声明一个基础 class,命名为当然 Transaction
,包含与 T
无关的所有成员。那么return值可以是Transaction
.
- What type does the class in "3." of the Transaction class need to be?
要将您的部门 class 与各种导出类型分离,我建议您让部门 class 实现一个通用接口。像这样:
public interface Exportable {
// return a list of attribute names, values, and types to export
IList<Tuple<String, String, Type>> GetAttributes();
}
例如:
public class Police : Exportable {
public IList<Tuple<String, String, Type>> GetAttributes() {
// return list size 3 - attribute info for Age, VehicleNumber, Supervisor
}
}
- Is the overall design appropriate? What other designs should I consider?
Transaction
class 设计似乎不太适合这个问题。
考虑一个 Export
class,每个导出类型都有一个方法,每个方法接收从 Exportable
接口方法返回的属性。基本大纲:
public static class Export {
public static boolean CSV(IList<Tuple<String, String, Type>> attributes) {
// export attributes to CSV, return whether succeeded
}
public static boolean Excel(IList<Tuple<String, String, Type>> attributes) {
// export attributes to Excel, return whether succeeded
}
// same thing for XML
}