设计模式之 C#为什么没有friend
为了名誉
这篇文章网络小乞丐写于2021/03/28
C#有friend多好呀
今天看一个视频在讲设计模式,思考起这个问题来了。我们在搭建框架的时候会用到一些设计模式,访问级别限制也是能用到的,比如单例模式让构造函数在外部访问不到就改成比较低的访问级别,
可是在工厂模式里面我就陷入了诡异的深思。
考虑这么一种情形
这个是一个比较常见的情形,以类BaseClass为父类的类家族和一个管理这个家族的管理器ClassManager组成一个比较合理的系统,最后在用户类CustomClass类里面通过管理器ClassManager使用类家族提供的功能。那么问题来了。
在工厂,或者状态之类的设计模式中,如何防止用户new类家族对象?
比如工厂模式,工厂可以生产袜子,生产衣服,这是你的功能,消费者生产不了,但是,关键是在程序编写的时候,程序员可以new一个袜子,new一个衣服呀?在c++里面可以通过友元来搞定,比如这样组织代码
class BaseClass
{
friend class ClassManager;//构造函数是保护的,所以外部类只有友元类可以创建
protected://通过保护级别来让用户类无法创建对象
BaseClass() {}
};
class AClass : public BaseClass
{
friend class ClassManager;//构造函数是保护的,所以外部类只有友元类可以创建
protected://通过保护级别来让用户类无法创建对象
AClass() {}
};
class BClass : public BaseClass
{
friend class ClassManager;//构造函数是保护的,所以外部类只有友元类可以创建
protected://通过保护级别来让用户类无法创建对象
BClass() {}
};
class ClassManager
{
private:
ClassManager() {}
public:
static ClassManager* Instance()
{
//简单的实现单例模式,不是讨论重点
static ClassManager instance;
return &instance;
}
//因为类家族对本类友元,所以可以创建类家族的实例
static BaseClass* CreateClass(int reason)
{
switch (reason)
{
case 0:
return new AClass();
break;
case 1:
return new BClass();
break;
default:
return new BaseClass();
break;
}
}
};
class CustomClass
{
public:
CustomClass() {}
public:
void LogicFunc()
{
BaseClass* pClass = ClassManager::Instance()->CreateClass(0);//正确
//AClass* pA = new AClass(); 错误,由于构造函数保护级别无法创建
}
};
是的,这样组织代码是可以编译通过的,类家族的实例只能通过类家族管理器创建,其他类一概不行,这样就限制了程序员随意创建类家族的对象了。
是什么起作用呢?就是友元friend。有什么好处呢?这样可以限制类家族的创建只能在类管理器里面创建,而且在设计模式里面一般都在一个地方创建,比如本例子就是在 CreateClass 函数中集中实现创建类家族的实例。
将来在程序维护中可以很明确知道在哪儿创建类家族实例,而且创建逻辑如果修改可以很集中的修改。比如类家族是一个充值类型之类的业务,需要每次记录时间,就可以修改CreateClass函数达到一次修改以后通用的目的。
如果不用友元friend呢?那么好了,总会有奇怪的人,随手写一个new让你无法预判。
情况反应到C#语言就让人捉急了,因为没有友元
class BaseClass
{
}
class AClass : BaseClass
{
}
class BClass : BaseClass
{
}
class ClassManager
{
public static BaseClass CreateClass(int reason)
{
switch (reason)
{
case 0:
return new AClass();
case 1:
return new BClass();
default:
return new BaseClass();
}
}
}
class CustomClass
{
void DoXXX()
{
BaseClass cls = ClassManager.CreateClass(0);//OK
AClass aCls = new AClass();//同样OK 但是不符合架构设计
}
}
怎么办?只能通过程序老大写文档了,或者耳提面命了。后来呢,只能靠自觉或者叫做职业素养了。
这就好比工厂说你只能从我这里拿到商品,国家也规定了,道德也约束了。但是你自己有能力创造这个商品,你相信程序员以后肯定会从工厂拿这些商品吗,尤其是那种有个性的程序猴子?
捉急呀,如果把构造函数添加访问级别,那么管理器都不能创建类家族成员了,如果没有访问级别,那么呵呵了,谁都可以创建呀。
如果,我是说如果,C#有友元该多好呀,这里友元不是破坏了封装性,而是保护了一个家族的内聚,不让他们随意分散呀!
解决方案
我们可以通过类家族的子类实现为内部类,类家族父类用抽象类或者接口来实现,代码如下
abstract class BaseClass
{
public abstract void Test();
}
class Manager
{
private class UClass : BaseClass
{
public override void Test()
{
Console.WriteLine("UClass");
}
}
private class AClass : BaseClass
{
public override void Test()
{
Console.WriteLine("AClass");
}
}
private class BClass : BaseClass
{
public override void Test()
{
Console.WriteLine("BClass");
}
}
public static BaseClass CreateClass(int reason)
{
switch (reason)
{
case 0:
return new AClass();
case 1:
return new BClass();
default:
return new UClass();
}
}
}
class CustomTest
{
public void Run()
{
BaseClass baseClass = Manager.CreateClass(0);
baseClass.Test();// OK 可以调用
//BaseClass b1 = new BaseClass(); 错误 抽象类无法实例化
//BaseClass b2 = new Manager.AClass(); 错误 访问级别限制 无法访问
}
}
通过这样的技术手段可以破解程序员随意new一个老婆的困境,当然了,程序猴子自己写一个老婆类怎么办呢,哈哈,那就是非法的喽