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

本文探讨了C#中缺乏友元(friend)关键字带来的问题,尤其是在使用设计模式如工厂模式时,如何限制类的外部创建。作者提出,通过将子类作为内部类并使用抽象基类或接口可以实现类似友元的功能,以保持类的封装性和内聚性。同时,这种方式可以避免程序员随意创建对象,提高代码的可控性和可维护性。
摘要由CSDN通过智能技术生成

设计模式之 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
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

当当小螳螂

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

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

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

打赏作者

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

抵扣说明:

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

余额充值