我的代码如下所示
public interface ICar
{
void Created();
}
public class BigCar : ICar
{
public void Created()
{
}
}
public class SmallCar : ICar
{
public void Created()
{
}
}
public class LuxaryCar : ICar
{
public void Created()
{
}
}
public class CarFactory
{
public ICar CreateCar(int carType)
{
switch (carType)
{
case 0:
return new BigCar();
case 1:
return new SmallCar();
case 2:
return new LuxaryCar();
default:
break;
}
return null;
}
}
在这段代码中,我有一个返回具体实例的工厂。但每次我需要有一个新的ICar接口实现时,我必须更改CarFactory的CreateCar()方法。我似乎不支持SOLID原则的开放封闭原则。请建议有更好的方法来处理这种情况。
答案 0 :(得分:5)
您可能希望将其配置为可配置,如下所示:
void Main()
{
// configurable array
var factories = new ICarFactory[] { new BigCarFactory() };
// create factory
var realfactory = new CarFactory(factories);
// create car
var car = realfactory.CreateCar(0);
}
public class CarFactory : ICarFactory
{
private ICarFactory[] _factories;
public CarFactory (ICarFactory[] factories)
{
_factories = factories;
}
public ICar CreateCar(int carType)
{
return _factories.Where(x=>x.SupportCar(carType)).First().CreateCar(carType);
}
public bool SupportCar(int type) => _factories.Any(x=>x.SupportCar(type));
}
public interface ICarFactory
{
ICar CreateCar(int type);
bool SupportCar(int type);
}
public class BigCarFactory : ICarFactory
{
public ICar CreateCar(int carType)
{
if(carType != 0) throw new NotSupportedException();
return new BigCar();
}
public bool SupportCar(int type) => type == 0;
}
public interface ICar
{
void Created();
}
public class BigCar : ICar
{
public void Created()
{
}
}
public class SmallCar : ICar
{
public void Created()
{
}
}
public class LuxaryCar : ICar
{
public void Created()
{
}
}
答案 1 :(得分:4)
有很多confusion around the open/closed principle, and what it should mean to your code。
事实就在这里,你很好。移动到配置文件只是将代码与数据分开并隐藏意图,这将导致问题。
答案是什么?这取决于。务实。 "只是f **王硬编码"。保持switch语句,使工厂的意图明确。保持数据和意图紧密相连,如此处所示。确保首先为缺少switch
个案例编写测试,并始终包含default
个案(即使它只是抛出异常)。
您的工厂只有一个改变的理由,只做一份工作。你现在的设计就好了。如果你发现你经常回来,添加新的案例,那么你可能想要考虑其他方式(配置文件不是答案,它只是将编辑移动到配置来自代码文件的文件)并重构您的解决方案。不断增加的转换语句并不好,但不是弄清楚如何以不同的方式做到这一点,而是花费精力试图从一开始就消除这个问题。见下面的附录:
另一种方法是没有多个类实现相同的接口。想想其他SOLID原则。也许还有另一种一致性或功能边界,而不是"类型的汽车"?也许有一组功能可以在更多,特定,细粒度的界面中指定,然后你可能会使用DI将实现注入你需要的地方,而不是必须首先使用工厂(其中,让我们面对它,无论如何只是一个捏造的构造函数)。另请参阅:"赞成合成而非继承"。
答案 2 :(得分:0)
似乎我的评论被误解了,所以我们走了:
假设我们有一些简单的配置文件:
<config>
<MyType key="1" type="My.Namespace.Ferrari"/>
<MyType key="2" type="My.Namespace.Fiat"/>
</config>
现在,您可以从该配置中读取并根据type-info:
创建实例ICar CreateCar(int carType)
{
foreach(var typeInfo in myConfig)
{
if(typeInfo.Key == carType) return Activator.CreateInstance(Type.GetType(typeInfo.type));
}
}
现在只需致电myFactory.CreateCare(2)
即可获得Fiat
的实例。
这肯定假设您已对序列进行反序列化(例如,使用Linq2Xml或使用XmlSerializer
),并且包含类型的程序集已在运行时加载。如果这不适用,您应该先致电Assembly.Load
。
现在你所有的工厂仍然需要做的是读取配置文件,最后加载程序集并构建一个字典来缓存密钥和类型,以提高性能。
依赖外部文件的优势在于您无需重新发送整个应用程序,只需重新发送更改的部分。
编辑:如果您的类型没有默认构造函数并且您必须提供args,那么事情可能会变得复杂。但是为了简单起见,我们假设我们拥有一个默认构造函数。答案 3 :(得分:0)
我认为弗拉德在评论中提出的建议是更好的方法,我在后续评论中提出了更具体的建议,但我在下面详细阐述。与tym32167's answer不同,此方法通过从应用程序发送到type
类型的ICarFactory
参数,不将您的库与特定应用程序结合使用本身。
换句话说,我不认为每当你需要添加新的实现时,tym32167的答案都会让你的课程“关闭以进行修改”。
public interface ICar {
string Name { get; }
}
class BigCar : ICar {
string Name {
get { return "Big Car"; }
}
}
class SmallCar : ICar {
string Name {
get { return "Small Car"; }
}
}
public interface ICarFactory {
ICar CreateCar();
}
public class BigCarFactory : ICarFactory {
ICar CreateCar() { return new BigCar (); }
}
public class SmallCarFactory : ICarFactory {
ICar CreateCar() { return new SmallCar (); }
}
请注意,他们不会遭受“控制耦合”,即他们不依赖外部“魔法”值来确定工厂的内部控制流程。换句话说,它们不是ICar CreateCar(int type);
,工厂只是ICar CreateCar();
。工厂不应通过type
参数与您的特定应用程序耦合,该参数具有特定于应用程序的含义。
现在在你的客户端应用程序中有一些任意方法,你有自己的汽车创建逻辑,即在你的库之外(例如,读取你的配置文件或其他东西),你可以做类似的事情:
public List<ICar> CreateCars() {
// organize your factories based on whatever value you want for 'type'
ICarFactory[] factories = { new BigCarFactory(), new SmallCarFactory() };
List<ICar> carsList = new List<>();
while (/* more config to read */ ) {
int type = // read type from config, etc.
carsList.Add(factories[type].CreateCar());
}
return carsList;
}
现在,每次添加新的ICar
实施时,您都会创建一个关联的ICarFactory
实施,这显然非常容易实现,并且您向ICarFactory[] factories
添加了一个新工厂数组在您想要的任何车辆type
位置,type
知识保留在您的特定应用程序中,与您的汽车/工厂库分离。
与其他回复相比,为什么我的提案 SOLID ?
type
意味着什么ICar
和ICarFactory
可以通过添加新实施进行扩展,但在执行此操作时不需要修改现有实现。ICar
个对象,因此无论实现是BigCar
还是{{}} {1}}或其他什么。SmallCar
参数将您的资源库与您的应用程序结合,该参数具有应用程序特定的含义