设计模式之 C# 为什么没有friend,唉~

设计模式之 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一个老婆的困境,当然了,程序猴子自己写一个老婆类怎么办呢,哈哈,那就是非法的喽

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

当当小螳螂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值