设计开篇
1 设计模式
类是我们面向对象编程的承载工具,可以说是面向对象的起点。
设计模式,这种算面向对象的进化。按照gof设计模式的分类
设计模式分为:创建型,结构型,行为型。
其中创建型主要和类的创建有关
结构性组织扩展类和类之间的关系
行为型主要扩展的类的访问
这三个对应到类上
创建型模式对应的是构造函数
结构型对应的是类的属性
行为型对应类的方法
就想我们以前学数学中很多证明题,都源自最基本的定理,面向对象编程也有类似的地方
设计模式就是类最基本功能的一个进化
2.依赖注入
相信大家对Ioc(依赖注入)肯定不陌生,Ioc主要遵循设计原则中的依赖倒置原则,
但是假设我们不要把这个东西提升到设计的高度,只看它的功能就会发现, Ioc创建类正好就是控制类的构造函数,和设计模式中创建型模式有关,
例如创建型模式中单例模式用ioc生命周期管理可以达到同样的效果,
这里以Unity为例,Untiy支持child容器。利用child容器,我们可以在运行时提供更多动态创建的内容。
我们以asp.net mvc为例,我们可以在session初始化的时候根据不同用户注入不同接口的实现。
例如我们可以在repository中注入一个默认的规约,当不同用户登录 我们可以将用户对数据的访问权限形成一个规约,
注册到child容器中,然后在controller激活的时候使用当前session的子容器来激活controller。
在这里使用例子中,Ioc又实现了类似factory,甚至是building模式的功能(这个例子会在后面的文章中给大家展现具体的实现).
3.抽象的维度
多态是代码的下行
抽象是代码的下行
如果B继承自A,C继承自A,那么A称之为B,C这个维度的抽象,B,C称之为A这个维度的变化 A,B,C称之为一个维度的继承关系
传统的继承解决的是一个维度的变化,如果我们在这个维度上面引入泛型,并且用where限制泛型的行为 或者属性,
那么就可以用诸如interface<T1,T2,T3>这样,通过组合几个泛型提供多个维度的变化。 此处用泛型扩展的继承有点类似于多继承。
4.元数据编程(Attribute)
Attribute在我所使用的元数据编程占有重要位置。
在asp.net mvc中,ValueProvider,ModuleBinding,Validator,Filter,都使用了元数据编程。
不谈这个大的我们看一个更小的例子。
最早在winform中使用DateGrid的时候,我们都是在grid设计器中去设置列的属性,名称,绑定的字段,等等。
如果我们引入元数据编程,就可以通过在类的属性上加上特性来扩展。
这样做更便于维护,当你的界面需要修改时,只需要在绑定的类上做修改,修改的地方相对集中,而不用去操作设计器。
5. 策略工厂
上面说了很多理论的东西,下面为大家带来点干货。
首先我们分析一个简单工厂的代码:
public interface ITest { void DoSomething(); } public class Test1: ITest { public void DoSomething() { } } public class Test2: ITest { public void DoSomething() { } } public class Factory { public static ITest Create(string type) { if (type == "1") return new Test1(); if (type == "2") return new Test2(); throw new NotImplementedException(); } }
分析简单工厂的弊端
1,扩展不方便需要改动create的逻辑
2,客户需要知道create的具体逻辑
3,创建类型过于单一导致简单工厂类的泛滥
4,无法为对象创建提供灵活性,例如构造函数参数
5,无法实现运行时扩展
下面我对简单工厂做了一些改造
public interface IStrategy { } public interface IGenericsFactory<TStrategy> where TStrategy : IStrategy { /// <summary> /// 注册处理策略 /// </summary> /// <param name="name"></param> /// <returns></returns> void Regist(Type type); /// <summary> /// 获取处理策略 /// </summary> /// <param name="name"></param> /// <returns></returns> TStrategy GetStrategy(string name); /// <summary> /// 获取所有的处理策略 /// </summary> /// <param name="name"></param> /// <returns></returns> List<string> GetStrategys(); } public class GenericsFactory<TStrategy> : IGenericsFactory<TStrategy> where TStrategy : IStrategy { public GenericsFactory() { if (_strategys == null) { _strategys = GetType().Assembly .GetTypes() .Where(item => item.GetInterfaces().Contains(typeof(TStrategy)) || item.IsSubclassOf(typeof(TStrategy))) .Where(item => !item.IsAbstract) .ToDictionary(item => { var desc = item.GetCustomAttribute<DescriptionAttribute>(); return desc != null ? desc.Description : string.Empty; }, item => item); } } protected Dictionary<string, Type> _strategys; public void Regist(Type type) { if (type.GetInterfaces().Contains(typeof(TStrategy)) && type.IsSubclassOf(typeof(TStrategy))) throw new TypeLoadException(string.Format("类型不是从{0},继承",typeof(TStrategy).Name)); if (_strategys.ContainsKey(MetaDataManager.Attribute.GetDescription(type))) return; _strategys.Add(MetaDataManager.Attribute.GetDescription(type), type); } public TStrategy GetStrategy(string name) { if (!_strategys.ContainsKey(name)) _strategys.Add(name, typeof(TStrategy)); return (TStrategy)UnityService.Resolve(_strategys[name]); } public List<string> GetStrategys() { return _strategys.Keys.ToList(); } }
改造思路
1,使用将create的类型标记在对应的接口实现上,反射获取子类的特性,如果新增加一个类型只
需要增加子类,并且在子类添加特性
2,将所有可用子类的元素据缓存起来,在工厂构造函数中反射所有子类和子类的特性用字典缓存起来
并且所有可创建的类型暴露给外部
3,将构造出来的类型做成泛型,并且提供泛型限制,将可构造类型独立出一个维度来变化
4,通过ioc来动态创建类型
5,通过提供regist来提供运行时扩展
使用的案例:
1,SSO登陆,前台用户和后台用户,存放在不同的数据表,数据结构也可能不一样
2,动态数据权限配置,例如,某个角色只能访问某部分数据 =,>,<等等这些筛选条件的扩展
3,状态者模式,状态可扩展,可以由界面去选择某种状态对应那种处理方式,
将处理方式类型放在数据库
4,责任链模式中节点的扩展和配置
通过缓存可以直接所有节点,灵活配置节点来实现流程的功能
5,装饰器的扩展
可以搭配装饰器模式配置出先用某个装饰后用某个装饰、
总结:
上面的这些关于程序设计的一些思考,会在后面的文章中分享一些自己在实际项目中的
具体案例,请关注后续文章。要想一个架构设计能顺利推广,最简单粗暴的方法就是这个架构能够帮助程序员少些代码。
下一篇文章将分享一个,全栈式编程的设计,主要应用于后台系统的增删改查,方便程序员更
快速的处理掉数据的增删改查这部分通用逻辑