没有开关的工厂模式或 If/Then
Factory Pattern without a Switch or If/Then
我正在寻找一个如何实现工厂的简单示例 class,但是没有 使用 Switch 或 If-Then 语句。我能找到的所有示例都使用一个。例如,如何修改这个简单的示例(如下),使实际的工厂不依赖于 Switch?在我看来,这个例子违反了 Open/Close 原则。我希望能够添加具体的 classes('Manager'、'Clerk'、'Programmer' 等)而无需修改工厂 class。
谢谢!
class Program
{
abstract class Position
{
public abstract string Title { get; }
}
class Manager : Position
{
public override string Title
{
get { return "Manager"; }
}
}
class Clerk : Position
{
public override string Title
{
get { return "Clerk"; }
}
}
class Programmer : Position
{
public override string Title
{
get { return "Programmer"; }
}
}
static class Factory
{
public static Position Get(int id)
{
switch (id)
{
case 0: return new Manager();
case 1: return new Clerk();
case 2: return new Programmer();
default: return new Programmer();
}
}
}
static void Main(string[] args)
{
for (int i = 0; i <= 2; i++)
{
var position = Factory.Get(i);
Console.WriteLine("Where id = {0}, position = {1} ", i, position.Title);
}
Console.ReadLine();
}
}
更新:
哇!感谢大家!我学到了很多。在审查了所有反馈后,我混合了一些答案并得出了这个结论。我愿意就更好的方法进行进一步的对话。
class Program
{
public interface IPosition
{
string Title { get; }
}
class Manager : IPosition
{
public string Title
{
get { return "Manager"; }
}
}
class Clerk : IPosition
{
public string Title
{
get { return "Clerk"; }
}
}
class Programmer : IPosition
{
public string Title
{
get { return "Programmer"; }
}
}
static class PositionFactory
{
public static T Create<T>() where T : IPosition, new()
{
return new T();
}
}
static void Main(string[] args)
{
IPosition position0 = PositionFactory.Create<Manager>();
Console.WriteLine("0: " + position0.Title);
IPosition position1 = PositionFactory.Create<Clerk>();
Console.WriteLine("1: " + position1.Title);
IPosition position2 = PositionFactory.Create<Programmer>();
Console.WriteLine("1: " + position2.Title);
Console.ReadLine();
}
}
另一个编辑:
也可以使用未知类型创建接口实例:
static class PositionFactory
{
public static IPosition Create(string positionName)
{
Type type = Type.GetType(positionName);
return (IPosition)Activator.CreateInstance(type);
}
}
然后可以这样调用:
IPosition position = PositionFactory.Create("Manager");
Console.WriteLine(position.Title);
您可以使用自定义属性和反射。
[PositionType(1)]
class Manager : Position
{
public override string Title
{
get
{ return "Manager"; }
}
}
[PositionType(2)]
class Clerk : Position
{
public override string Title
{
get
{ return "Clerk"; }
}
}
然后在您的工厂中,您可以获得所有继承自 Position
的 类,并找到具有正确值的 PositionType
属性的那个。
static class Factory
{
public static Position Get(int id)
{
var types = typeof(Position).Assembly.GetTypes()
.Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(Position)))
.ToList();
Position position = null;
foreach(var type in types)
{
type.GetCustomAttributes<PositionTypeAttribute>();
if(type.PositionId == id)
{
position = Activator.CreateInstance(type) as Position;
break;
}
}
if(position == null)
{
var message = $"Could not find a Position to create for id {id}.";
throw new NotSupportedException(message);
}
return position;
}
}
public class PositionFactory
{
private Dictionary<int, Type> _positions;
public PositionFactory()
{
_positions = new Dictionary<int, Type>();
}
public void RegisterPosition<PositionType>(int id) where PositionType : Position
{
_positions.Add(id, typeof(PositionType));
}
public Position Get(int id)
{
return (Position) Activator.CreateInstance(_positions[id]);
}
}
这样使用:
var factory = new PositionFactory();
factory.RegisterPosition<Manager>(0);
factory.RegisterPosition<Clerk>(1);
Position p = factory.Get(0); //Returns a new Manager instance
这个怎么样(不需要词典,请注意,如果您尝试 Create<Position>()
会出现语法错误):
EDIT - 更新为使用显式实现的 IPosition 接口。只有 IPosition 的实例可以访问成员函数(例如 <implementation of Manager>.Title
将无法编译)。
编辑 #2 Factory.Create 正确使用界面时应该 return IPosition 而不是 T。
using System;
using System.Collections.Generic;
class Program
{
interface IPosition
{
string Title { get; }
bool RequestVacation();
}
class Manager : IPosition
{
string IPosition.Title
{
get { return "Manager"; }
}
bool IPosition.RequestVacation()
{
return true;
}
}
class Clerk : IPosition
{
int m_VacationDaysRemaining = 1;
string IPosition.Title
{
get { return "Clerk"; }
}
bool IPosition.RequestVacation()
{
if (m_VacationDaysRemaining <= 0)
{
return false;
}
else
{
m_VacationDaysRemaining--;
return true;
}
}
}
class Programmer : IPosition
{
string IPosition.Title
{
get { return "Programmer"; }
}
bool IPosition.RequestVacation()
{
return false;
}
}
static class Factory
{
public static IPosition Create<T>() where T : IPosition, new ()
{
return new T();
}
}
static void Main(string[] args)
{
List<IPosition> positions = new List<IPosition>(3);
positions.Add(Factory.Create<Manager>());
positions.Add(Factory.Create<Clerk>());
positions.Add(Factory.Create<Programmer>());
foreach (IPosition p in positions) { Console.WriteLine(p.Title); }
Console.WriteLine();
Random rnd = new Random(0);
for (int i = 0; i < 10; i++)
{
int index = rnd.Next(3);
Console.WriteLine("Title: {0}, Request Granted: {1}", positions[index].Title, positions[index].RequestVacation());
}
Console.ReadLine();
}
}
为什么要把事情复杂化?
这是一个简单的解决方案:
using System;
using System.Collections.Generic;
class Program
{
interface IPosition
{
string Title { get; }
}
class Manager : IPosition
{
public string Title
{
get { return "Manager"; }
}
}
class Clerk : IPosition
{
public string Title
{
get { return "Clerk"; }
}
}
class Programmer : IPosition
{
public string Title
{
get { return "Programmer"; }
}
}
class Factory
{
private List<IPosition> positions = new List<IPosition>();
public Factory()
{
positions.Add(new Manager());
positions.Add(new Clerk());
positions.Add(new Programmer());
positions.Add(new Programmer());
}
public IPosition GetPositions(int id)
{
return positions[id];
}
}
static void Main(string[] args)
{
Factory factory = new Factory();
for (int i = 0; i <= 2; i++)
{
var position = factory.GetPositions(i);
Console.WriteLine("Where id = {0}, position = {1} ", i, position.Title);
}
Console.ReadLine();
}
}
下面是完全不使用工厂 class 的方法:
using System;
using System.Collections.Generic;
class Program
{
interface IPosition
{
string Title { get; }
}
class Manager : IPosition
{
public string Title
{
get { return "Manager"; }
}
}
class Clerk : IPosition
{
public string Title
{
get { return "Clerk"; }
}
}
class Programmer : IPosition
{
public string Title
{
get { return "Programmer"; }
}
}
static void Main(string[] args)
{
List<IPosition> positions = new List<IPosition> { new Manager(), new Clerk(), new Programmer(), new Programmer() };
for (int i = 0; i <= 2; i++)
{
var position = positions[i];
Console.WriteLine("Where id = {0}, position = {1} ", i, position.Title);
}
Console.ReadLine();
}
}
我正在寻找一个如何实现工厂的简单示例 class,但是没有 使用 Switch 或 If-Then 语句。我能找到的所有示例都使用一个。例如,如何修改这个简单的示例(如下),使实际的工厂不依赖于 Switch?在我看来,这个例子违反了 Open/Close 原则。我希望能够添加具体的 classes('Manager'、'Clerk'、'Programmer' 等)而无需修改工厂 class。
谢谢!
class Program
{
abstract class Position
{
public abstract string Title { get; }
}
class Manager : Position
{
public override string Title
{
get { return "Manager"; }
}
}
class Clerk : Position
{
public override string Title
{
get { return "Clerk"; }
}
}
class Programmer : Position
{
public override string Title
{
get { return "Programmer"; }
}
}
static class Factory
{
public static Position Get(int id)
{
switch (id)
{
case 0: return new Manager();
case 1: return new Clerk();
case 2: return new Programmer();
default: return new Programmer();
}
}
}
static void Main(string[] args)
{
for (int i = 0; i <= 2; i++)
{
var position = Factory.Get(i);
Console.WriteLine("Where id = {0}, position = {1} ", i, position.Title);
}
Console.ReadLine();
}
}
更新:
哇!感谢大家!我学到了很多。在审查了所有反馈后,我混合了一些答案并得出了这个结论。我愿意就更好的方法进行进一步的对话。
class Program
{
public interface IPosition
{
string Title { get; }
}
class Manager : IPosition
{
public string Title
{
get { return "Manager"; }
}
}
class Clerk : IPosition
{
public string Title
{
get { return "Clerk"; }
}
}
class Programmer : IPosition
{
public string Title
{
get { return "Programmer"; }
}
}
static class PositionFactory
{
public static T Create<T>() where T : IPosition, new()
{
return new T();
}
}
static void Main(string[] args)
{
IPosition position0 = PositionFactory.Create<Manager>();
Console.WriteLine("0: " + position0.Title);
IPosition position1 = PositionFactory.Create<Clerk>();
Console.WriteLine("1: " + position1.Title);
IPosition position2 = PositionFactory.Create<Programmer>();
Console.WriteLine("1: " + position2.Title);
Console.ReadLine();
}
}
另一个编辑:
也可以使用未知类型创建接口实例:
static class PositionFactory
{
public static IPosition Create(string positionName)
{
Type type = Type.GetType(positionName);
return (IPosition)Activator.CreateInstance(type);
}
}
然后可以这样调用:
IPosition position = PositionFactory.Create("Manager");
Console.WriteLine(position.Title);
您可以使用自定义属性和反射。
[PositionType(1)]
class Manager : Position
{
public override string Title
{
get
{ return "Manager"; }
}
}
[PositionType(2)]
class Clerk : Position
{
public override string Title
{
get
{ return "Clerk"; }
}
}
然后在您的工厂中,您可以获得所有继承自 Position
的 类,并找到具有正确值的 PositionType
属性的那个。
static class Factory
{
public static Position Get(int id)
{
var types = typeof(Position).Assembly.GetTypes()
.Where(t => !t.IsAbstract && t.IsSubclassOf(typeof(Position)))
.ToList();
Position position = null;
foreach(var type in types)
{
type.GetCustomAttributes<PositionTypeAttribute>();
if(type.PositionId == id)
{
position = Activator.CreateInstance(type) as Position;
break;
}
}
if(position == null)
{
var message = $"Could not find a Position to create for id {id}.";
throw new NotSupportedException(message);
}
return position;
}
}
public class PositionFactory
{
private Dictionary<int, Type> _positions;
public PositionFactory()
{
_positions = new Dictionary<int, Type>();
}
public void RegisterPosition<PositionType>(int id) where PositionType : Position
{
_positions.Add(id, typeof(PositionType));
}
public Position Get(int id)
{
return (Position) Activator.CreateInstance(_positions[id]);
}
}
这样使用:
var factory = new PositionFactory();
factory.RegisterPosition<Manager>(0);
factory.RegisterPosition<Clerk>(1);
Position p = factory.Get(0); //Returns a new Manager instance
这个怎么样(不需要词典,请注意,如果您尝试 Create<Position>()
会出现语法错误):
EDIT - 更新为使用显式实现的 IPosition 接口。只有 IPosition 的实例可以访问成员函数(例如 <implementation of Manager>.Title
将无法编译)。
编辑 #2 Factory.Create 正确使用界面时应该 return IPosition 而不是 T。
using System;
using System.Collections.Generic;
class Program
{
interface IPosition
{
string Title { get; }
bool RequestVacation();
}
class Manager : IPosition
{
string IPosition.Title
{
get { return "Manager"; }
}
bool IPosition.RequestVacation()
{
return true;
}
}
class Clerk : IPosition
{
int m_VacationDaysRemaining = 1;
string IPosition.Title
{
get { return "Clerk"; }
}
bool IPosition.RequestVacation()
{
if (m_VacationDaysRemaining <= 0)
{
return false;
}
else
{
m_VacationDaysRemaining--;
return true;
}
}
}
class Programmer : IPosition
{
string IPosition.Title
{
get { return "Programmer"; }
}
bool IPosition.RequestVacation()
{
return false;
}
}
static class Factory
{
public static IPosition Create<T>() where T : IPosition, new ()
{
return new T();
}
}
static void Main(string[] args)
{
List<IPosition> positions = new List<IPosition>(3);
positions.Add(Factory.Create<Manager>());
positions.Add(Factory.Create<Clerk>());
positions.Add(Factory.Create<Programmer>());
foreach (IPosition p in positions) { Console.WriteLine(p.Title); }
Console.WriteLine();
Random rnd = new Random(0);
for (int i = 0; i < 10; i++)
{
int index = rnd.Next(3);
Console.WriteLine("Title: {0}, Request Granted: {1}", positions[index].Title, positions[index].RequestVacation());
}
Console.ReadLine();
}
}
为什么要把事情复杂化? 这是一个简单的解决方案:
using System;
using System.Collections.Generic;
class Program
{
interface IPosition
{
string Title { get; }
}
class Manager : IPosition
{
public string Title
{
get { return "Manager"; }
}
}
class Clerk : IPosition
{
public string Title
{
get { return "Clerk"; }
}
}
class Programmer : IPosition
{
public string Title
{
get { return "Programmer"; }
}
}
class Factory
{
private List<IPosition> positions = new List<IPosition>();
public Factory()
{
positions.Add(new Manager());
positions.Add(new Clerk());
positions.Add(new Programmer());
positions.Add(new Programmer());
}
public IPosition GetPositions(int id)
{
return positions[id];
}
}
static void Main(string[] args)
{
Factory factory = new Factory();
for (int i = 0; i <= 2; i++)
{
var position = factory.GetPositions(i);
Console.WriteLine("Where id = {0}, position = {1} ", i, position.Title);
}
Console.ReadLine();
}
}
下面是完全不使用工厂 class 的方法:
using System;
using System.Collections.Generic;
class Program
{
interface IPosition
{
string Title { get; }
}
class Manager : IPosition
{
public string Title
{
get { return "Manager"; }
}
}
class Clerk : IPosition
{
public string Title
{
get { return "Clerk"; }
}
}
class Programmer : IPosition
{
public string Title
{
get { return "Programmer"; }
}
}
static void Main(string[] args)
{
List<IPosition> positions = new List<IPosition> { new Manager(), new Clerk(), new Programmer(), new Programmer() };
for (int i = 0; i <= 2; i++)
{
var position = positions[i];
Console.WriteLine("Where id = {0}, position = {1} ", i, position.Title);
}
Console.ReadLine();
}
}