给你写了个小小的示例,自己看一看吧,看看有启发没有。 public abstract class IPage { public void LoadPage() { m1(); m2(); m3(); } public abstract void m1(); public abstract void m2(); public abstract void m3(); } public class BaseGridPage : IPage { public override void m1() { throw new NotImplementedException(); } public override void m2() { throw new NotImplementedException(); } public override void m3() { throw new NotImplementedException(); } } public class BaseFieldPage : IPage { public override void m1() { throw new NotImplementedException(); } public override void m2() { throw new NotImplementedException(); } public override void m3() { throw new NotImplementedException(); } } public class BaseChartPage : IPage { public override void m1(){} public override void m2(){throw new NotImplementedException();} public override void m3(){throw new NotImplementedException();} } public class UIPage<T> where T : IPage,new() { public T _page; public UIPage() { _page=new T(); } } public class form1 : UIPage<BaseGridPage> { public void Load() { _page.LoadPage(); } }
在我看来主要的问题是在m1、m2、m3这些功能里面每个都耦合了三种配置方案,应该是每种方案的每个功能使用独立的类实现,让功能这个概念本身和配置方案无关。而不是仅仅按照配置方案把公共的部分抽出来。我重新定义下(为简单使用2*2,而不是3*3): 方案(Plan):对应你说的配置方案,由IPlan接口定义,BasePlan抽象基类实现公共功能,Plan1、Plan2是两种具体方案。UI使用IPlan接口。 行为(Behavior):对应你说的N种功能,由IBehaviorN接口定义,每种行为由一个方案无关的抽象基类实现这种行为的公共功能,比如BaseBehavior1、BaseBehavior2,然后具体类Plan1Behavior1是对应Plan1的Behavior1实现,Plan2Behavior1是对应Plan2的Behavior1实现,等等。这里需要注意,未必是m个方案n种行为最后就有m*n个具体行为类,而是根据需要,很可能可以共用。如下图:这样,具体方案和具体行为在结构上没有直接关系,而是在BasePlan上通过接口组合多个行为。这个图里我没画你抽出来的针对每个方案的工具类,那种工具不属于结构组成部分。接下来如何选择具体的方案,还有组合哪些具体的行为可以用一个PlanFactory来通过配置文件或者attribute的定义来实现。如下图:如果通过attribute,那就是一个PlanAttribute,放在具体方案类上来定义方案的名字,一个BehaviorAttribute,放在具体行为类上来定义它属于哪个方案。这样,使用时就 var pf = new PlanFactory("p1"); var plan = pf.Create();,或者定义一个Plan静态类,var plan = Plan.Create("p1");就可以了。PlanFactory里面通过给定的名字查找并创建所有需要的行为,把这些行为作为构造参数来创建具体的方案。 这种结构如果使用IoC/DI框架来注册和创建那更简单,都不需要自己写attribute和factory。关于不同客户对于方案的使用方式不一样,如何不一样没有清楚的说明,我也就没有考虑。这种不一样如果能够定义清楚,那么每个客户对于每个方案定义策略,策略也作为参数来构造方案。关于对行为扩展,一般来说不同行为由于功能不同,接口不容易统一。方案依赖于每一个行为接口,如果多了一种行为,那方案类从基类到具体实现都必须修改才可能支持这多出来的行为。除非这些行为是同质的,可以用统一的接口定义,方案中仅仅是按照一定顺序一个个调用,或者基于一定策略选择用不用,那样增加行为就不会影响方案。
1.首先确定你业务里面哪些是不变的,放在m1,2,3里面。会变的,在继承的类里面去重写,我感觉你这业务并不复杂,只是你没有把哪些是要变的业务理清楚,这步最重要,提炼业务。 public abstract class IPage { public void LoadPage() { BeforM1(); m1(); AfterM1(); //..... m2(); //...... m3(); } public virtual void BeforM1(){} public abstract void m1(); public virtual void AfterM1() { } public virtual void BeforM2() { } public abstract void m2(); public virtual void AfterM2() { } public virtual void BeforM3() { } public abstract void m3(); public virtual void AfterM3() { } } public class BaseGridPage : IPage { public override void m1(){} public override void m2(){} public override void m3(){} } public class BaseFieldPage : IPage { public override void m1(){} public override void m2(){} public override void m3(){} } public class BaseChartPage : IPage { public override void m1() { } public override void m2() { } public override void m3() { } } /*这个是一种模式了。 public class UIPage<T> where T : IPage, new() { public T _page; public UIPage() { _page = new T(); } } public class form1 : UIPage<BaseGridPage> { public void Load() { _page.LoadPage(); //print custom data 4+? }
} */ public class CustomClass:BaseGridPage { public override void BeforM1() { base.BeforM1(); //Do Curstom Special Operation } }
如果每个客户可以配置的话,你就去实现委托,在系统加载之初根据客户的配置,利用反射将委托事件关联,然后后面执行就不存在每次都反射的情况,只要你的实体保存下来就行了。public delegate void DoSomeThing(); public event DoSomeThing BeforeM1;
不是组合,是三个不同应用的,但每个应该都有m1,2,3三个功能,只是最后的实例不一样
可能我没说清楚,举个例子:现在我系统(生物)里有三个应用(三个种类ac,ap,am)分别为人,陆地动物,海生动物,第个种类都有吃,喝,走(m1,m2,m3),只不过方式不一样罢了。
我UI中定义It是人类的话实现对应的。定义陆地动物的话实现对应。。怎么调用是UI决定的,类库只是集成他们三个,方便维护。如同VS中有textbox,label.....但用那些控件由使用者决定,但VS系统类库中textbox,label...这些控件都得实现
谢谢提点,我设计Im接口就是方便c1,p1,a1调用方便,但m1,2,3又需要ac,ap,am中的属性方法。你说的用 sealed stati应该能解决这个问题,但是有点不甘心,请问我上面设计问题出现在那里,是思路不对还是把问题复杂化了还是其它。
聚合可能不行吧,我现在想把m1,2,3频繁修改的分出来。
如果要是m1,2,3和每个基类聚合的话,要扩展m功能会不会有困难,比哪我还有m4,5,6......
如果把基类和m1,2,3都聚合那不就不用设计了我是这样理解的,请问还有没有其它解决方法
C1就是一个工厂
你的所有Mxx都是从IM接口派生的。
IM{
Ac{get;set;}
Ap{get;set;}
Am{get;set;}
}M1:IM{
Ac{get;set;}
Ap{get;set;}
Am{get;set;}
}//其他的ModelC1Factory{
IM CreateModel(int state){
//这里可以用反射
switch(state){
case 1:
return new M1();
//...其他情况
}
}
}
这样是可以实现,但这样虽然将m1,2,3分开了,但那三个基类又分不开了,因为我系统配置成什么样的就调用什么样的基类,比如我将c1配置成用ac,那这个系统就是一个ac系统所以调用的m1,2,3也是用ac基类,这样的话其它二个基类就没必要了,相当于没有。如果我c1配置成用ap,同样道理m1,2,3实现ap基类,另二个基类没用了。你的这个方法可能还不如sp1234说的将基类密封起来合适。我的系统中其实主要就是实现m1,2,3的功能,但有三种不同配置,用户要求那一种我就用那一种基类实现m1,2,3所以m1,2,3改动比较频繁,但UI层不用动,基类不用动,只是这种关系和实现我组织不起来。可能我表述不太清楚,麻烦你帮帮忙,连同上面的二张图看看,第一张是一种想法,第二张是另一种想法,不知道那张图合适我这样情况,或者都不合适,我的这种情况 要怎么组织。原来这个系统是就一个总类传参给m1,2,3通知m1,2,3使用那一种。这样造成了m1,2,3比较大而且每次修改都需要找来找去的,所以我就将不修改的三种配置提成三个基类了,但m1,2,3修改的部分又依赖提出去的部分。纠结啊。。帮帮忙
{
public void LoadPage()
{
m1();
m2();
m3();
}
public abstract void m1();
public abstract void m2();
public abstract void m3();
} public class BaseGridPage : IPage
{
public override void m1()
{
throw new NotImplementedException();
} public override void m2()
{
throw new NotImplementedException();
} public override void m3()
{
throw new NotImplementedException();
}
} public class BaseFieldPage : IPage
{
public override void m1()
{
throw new NotImplementedException();
} public override void m2()
{
throw new NotImplementedException();
} public override void m3()
{
throw new NotImplementedException();
}
} public class BaseChartPage : IPage
{
public override void m1(){}
public override void m2(){throw new NotImplementedException();}
public override void m3(){throw new NotImplementedException();}
} public class UIPage<T> where T : IPage,new()
{
public T _page; public UIPage()
{
_page=new T();
}
} public class form1 : UIPage<BaseGridPage>
{
public void Load()
{
_page.LoadPage();
}
}
你这个能实现我的功能,但是扩展不太容易吧,我要是再加m4,5,6呢,还得修改那个抽象类。
我是想将不修改的都封到一个类库中,但能力不够,可能就楼上sp1234说的用密封方式比较接近我的想法。
可能我说的不完全,一段一段的,大家看的不方便,我现在重新整理下来说说,希望大家帮我解决下这个问题,谢谢!1.程序功能:
我现有个程序三种配置方案为了方便描述我称它为a1,a2,a3。根据不同的需求配置不同的方案(每次配置只配置一种,相当于另外二种没用,举例:就如同VS中的textbox,label等等控件一样,我只使用textbox但vs系统中还是得把label控件也得实现)。但不管是那种方案都得实现m1,2,3三个功能(也许以后还扩展m5,6,7功能)但目前就只有m1,2,3三个功能。因为配置方案不同实现m1,2,3所需要的前提就不一样。2.现有程序做法:
一个总类通过参数switch判断是那一种配置分别调用,举例:现在配置的a1方案,那么总类得知情况后,就调用m1中的a1配置相关方法,m2中的a1配置相关方法,m3中的a1配置相关方法。(m1,2,3分别将三种配置方案都实现了)。3.困扰:
每个方案都不是固定的,就拿a1方案来说吧,a使用者和b使用者在使用a1这个方案的时候要求也不一样(也可以说是客户),这样造成了m1,2,3比较大每次修改都需要找来找去的,十分不方便,程序组织也比较乱。4.我的改进和出现的问题
将m1,2,3中的三种配置分别的公共部分做成三个基类,每一种配置做成一个基类(也就是m1中的a1配置,m2中的a1配置,m3中的a1配置公共部分提取成基类,a2,a3配置方案也分别提取成基类)。这样m1,2,3就只剩下频繁修改的部分了可以放到UI层去频繁的改动,其它的可以封到一个类库中。
但这样提取出去后倒致m1,2,3修改的部分又依赖提出去的部分(基类)。基类又不能被继承(不能继承的原因:提取出去的基类因为有部分属性方法特殊只能被初始化一次,叫作基类实际是方便描述,也可以认为是和工具类差不多的一个类)啊啊啊,没法弄了,求助!!!
方案(Plan):对应你说的配置方案,由IPlan接口定义,BasePlan抽象基类实现公共功能,Plan1、Plan2是两种具体方案。UI使用IPlan接口。
行为(Behavior):对应你说的N种功能,由IBehaviorN接口定义,每种行为由一个方案无关的抽象基类实现这种行为的公共功能,比如BaseBehavior1、BaseBehavior2,然后具体类Plan1Behavior1是对应Plan1的Behavior1实现,Plan2Behavior1是对应Plan2的Behavior1实现,等等。这里需要注意,未必是m个方案n种行为最后就有m*n个具体行为类,而是根据需要,很可能可以共用。如下图:这样,具体方案和具体行为在结构上没有直接关系,而是在BasePlan上通过接口组合多个行为。这个图里我没画你抽出来的针对每个方案的工具类,那种工具不属于结构组成部分。接下来如何选择具体的方案,还有组合哪些具体的行为可以用一个PlanFactory来通过配置文件或者attribute的定义来实现。如下图:如果通过attribute,那就是一个PlanAttribute,放在具体方案类上来定义方案的名字,一个BehaviorAttribute,放在具体行为类上来定义它属于哪个方案。这样,使用时就 var pf = new PlanFactory("p1"); var plan = pf.Create();,或者定义一个Plan静态类,var plan = Plan.Create("p1");就可以了。PlanFactory里面通过给定的名字查找并创建所有需要的行为,把这些行为作为构造参数来创建具体的方案。
这种结构如果使用IoC/DI框架来注册和创建那更简单,都不需要自己写attribute和factory。关于不同客户对于方案的使用方式不一样,如何不一样没有清楚的说明,我也就没有考虑。这种不一样如果能够定义清楚,那么每个客户对于每个方案定义策略,策略也作为参数来构造方案。关于对行为扩展,一般来说不同行为由于功能不同,接口不容易统一。方案依赖于每一个行为接口,如果多了一种行为,那方案类从基类到具体实现都必须修改才可能支持这多出来的行为。除非这些行为是同质的,可以用统一的接口定义,方案中仅仅是按照一定顺序一个个调用,或者基于一定策略选择用不用,那样增加行为就不会影响方案。
谢谢,你说的IBehavior1,IBehavior2二接口其实他们都是你所说的同质的,都做同一个动作,可以用一个接口表示吗?
举例,IBehavior1钢笔写字,IBehavior2毛笔写字,他们都是做“写”的动作(我的程序里是这样的)就是实现这个接口的依赖不同没办法给他传需要的,你说的除了“每种行为由一个方案无关的抽象基类实现这种行为的公共功能”这点不符合我要求外其它都符合。 为什么 不符合?因为我 说过了,那个抽出来的基类比较特殊只能初始化一次,不能被多次继承,多次继承会让他多次初始化。倒致除了第一个功能实例能成功其它的都成功不了,成功不了也就运行不起来。除 了sp12324说的密封有没有其它解决方法?麻烦你帮我解决下多个功能抽出来的的那个和方案无关的抽象基类只能初始化一次的问题。(可能我表述的复杂了,其实每个功能比较简单只做一种动作,每个功能中的这个动作又依赖一个可配置但又不变动的公共属性方法组成的类(可认为是基类),这个公共类又只能初始化一次,因为有多个功能要用,所以又不能做基类)sp1234说的密封倒是能解决但是除了密封或做成工具类以外有没有更好的解决方法了?就是做成像普通类一样的东西,我实例化对象他就占用资源供功能调用或可以给功能参数,我不实例化相当于我没有写过这个类。是不是我有点太异想天开或能力太弱弱。了
不知道你看前面的没有。我是想优化一个程序。我现在再细描述一下:
我有个程序有三种配置(板卡IO,OPC,RS485),不管是那种配置都实现三个功能(读,写,查)。
最后我配置OPC方案,那么读写查三个功能就会用到OPC相关属性方法。我配置RS485方案,那么读写查三个功能就会用到串口处理的相关属性方法。。
也就是说不管我配置那一 种都会影响到那三个功能,这是其一。其二就是有的客户要求在winform界面上输出四个数据,有的需要10个数据。。不同的客户不同的输出。所以倒致我这三个功能频繁修改,我想优化。
不知道你看前面的没有。我是想优化一个程序。我现在再细描述一下:
我有个程序有三种配置(板卡IO,OPC,RS485),不管是那种配置都实现三个功能(读,写,查)。
最后我配置OPC方案,那么读写查三个功能就会用到OPC相关属性方法。我配置RS485方案,那么读写查三个功能就会用到串口处理的相关属性方法。。
也就是说不管我配置那一 种都会影响到那三个功能,这是其一。其二就是有的客户要求在winform界面上输出四个数据,有的需要10个数据。。不同的客户不同的输出。所以倒致我这三个功能频繁修改,我想优化。这个功能反射加工厂模式实现不了?
不知道你看前面的没有。我是想优化一个程序。我现在再细描述一下:
我有个程序有三种配置(板卡IO,OPC,RS485),不管是那种配置都实现三个功能(读,写,查)。
最后我配置OPC方案,那么读写查三个功能就会用到OPC相关属性方法。我配置RS485方案,那么读写查三个功能就会用到串口处理的相关属性方法。。
也就是说不管我配置那一 种都会影响到那三个功能,这是其一。其二就是有的客户要求在winform界面上输出四个数据,有的需要10个数据。。不同的客户不同的输出。所以倒致我这三个功能频繁修改,我想优化。这个功能反射加工厂模式实现不了?虽然有点乱,我觉得可以实现
{
public void LoadPage()
{
BeforM1();
m1();
AfterM1();
//.....
m2();
//......
m3();
}
public virtual void BeforM1(){}
public abstract void m1();
public virtual void AfterM1() { }
public virtual void BeforM2() { }
public abstract void m2();
public virtual void AfterM2() { }
public virtual void BeforM3() { }
public abstract void m3();
public virtual void AfterM3() { }
} public class BaseGridPage : IPage
{
public override void m1(){}
public override void m2(){}
public override void m3(){}
} public class BaseFieldPage : IPage
{
public override void m1(){}
public override void m2(){}
public override void m3(){}
} public class BaseChartPage : IPage
{
public override void m1() { }
public override void m2() { }
public override void m3() { }
}
/*这个是一种模式了。
public class UIPage<T> where T : IPage, new()
{
public T _page; public UIPage()
{
_page = new T();
}
} public class form1 : UIPage<BaseGridPage>
{
public void Load()
{
_page.LoadPage();
//print custom data 4+?
}
}
*/
public class CustomClass:BaseGridPage
{
public override void BeforM1()
{
base.BeforM1();
//Do Curstom Special Operation
}
}
public event DoSomeThing BeforeM1;
不知道你看前面的没有。我是想优化一个程序。我现在再细描述一下:
我有个程序有三种配置(板卡IO,OPC,RS485),不管是那种配置都实现三个功能(读,写,查)。
最后我配置OPC方案,那么读写查三个功能就会用到OPC相关属性方法。我配置RS485方案,那么读写查三个功能就会用到串口处理的相关属性方法。。
也就是说不管我配置那一 种都会影响到那三个功能,这是其一。其二就是有的客户要求在winform界面上输出四个数据,有的需要10个数据。。不同的客户不同的输出。所以倒致我这三个功能频繁修改,我想优化。你这个最好用实时数据库的架构。
前台只和实时数据库打交道,后台配不同的IO驱动接不同的设备,
实时数据库的数据是可配置的,连接到不同的设备中,动态数据自动刷新。
组态软件本身是个通用的软件,它可以和很多种不同类型的PLC通信,可以走OPC,可以走串口,可以走以太网协议,不同PLC的以太网协议又不一样
人家的做法就是,不要把完全不相干的东西强行做到一个程序里
你想使用什么通信方式,安装对应的通信软件
通信软件有通用接口,组态软件只去跟通信软件通信获取数据,而不关心数据到底以什么方式获取出来
不管你是把这些配置放数据库里还是配到xml文档里,只要是能够配置的,可以写入,可以加载出来,然后显示相应的数据,其他不显示,就完了
就好比版主能使用删帖和修改功能,其他人只能发贴回贴一个道理
Unity里加载外部dll有很多麻烦,字符集也只支持UTF8,
所以通信部分我自己做了一个server,有2种通信,一种是读OPC,一种是读数据库
而Unity跟我的server通信是同一个协议,只不过其中一个字节表示到底要读OPC的数据还是数据库的数据,后面的数据格式完全是相同的格式,比如Unity中的物体名称,脚本名称,变量名称,变量类型,变量值.
它不用关心我的server端到底用了几种方式去读设备,它只需要知道server端发给它的数据如何解析就足够 了
我的业务是很简单的,我上面说过了:
还有就是程序运行初始化那三种配置只能是其中一种配置并且还只能一次,读、写、查三个功能都依赖于这个配置初始化后的属性方法。如果我初始化成OPC方案那么读、写、查三个功能就会用到OPC方案中的OPCOpen(),LinkServer...等属性方法,其中比如在读功能中object[] data=LinkServer.ReadGroup();
如果我初始化成板卡IO方案那么读、写、查三个功能就会用到板卡IO方案中的GetPortVal(),SetPortVal(),CardAddress等属性方法,其中比如在读功能中GetPortVal(CardAddress,ref data,1);同样是读这个功能,上面初始化成OPC、板卡IO二种不同的方案,这个功能的代码就会变化,但我不想让他的变动影响到其它的使用
我是说,你应该多借鉴成熟产品的做法如果你将不同的通信方式直接封装成不同的dll或者干脆就是不同的软件,只要它们实现的接口一样,这样不就行了
这样即使将来再有第4种,第5种,乃至第100种方式,也不过是再另外开发符合同样接口的dll或项目就可以了,而本身的主体完全不用改动
而你的通信软件应该作为服务端,它可能有M种通信方式你只需要根据不同的通信方式使用对应的服务端程序,然后让客户端和它通信,就行了
而不是自己组合M*N种方法出来
定义B类:接口1、接口2、接口3。
定义C类:接口1、接口2、接口3。调用的时候用反射+委托就可以了。
我不是做你说的通讯的,我做的是单机程序,和设备通讯,和不同的设备通讯所需要的API或Dll或自己写的属性方法类就不同。这个程序根据配置不同设备调用不同的处理用api或Dll或自己写的属性方法类。
求助,麻烦各位大侠耐心看下我在前面描述的问题
假如我用DataTable传输数据,不管是哪种通信模块,只要发来请求要数据,就返回一个DataTable,所有的通信模块都遵循这一个结构,也就是不管是OPC还是串口还是TCP还是http,读到数据之后组织成DataTable发给客户端(当然发送前需要序列化)
客户端得到数据之后反序列化成DataTable,然后根据每个客户端想要显示的内容不同,将DataTable组织成不同的形式,比如只显示4个,那么可能有些数据没用,就丢弃了,显示图表,就用数据填充进chart里
想做的更好,你协议也要做的更复杂点,比如只请求4个数据,你就不要把获得的全部数据都填充进DataTable里再返回或者DataTable改成实体类,或者自定义byte数组结构,多少到多少代表什么,客户端解析一下
客户端和服务端通信,可以放在一台机器上,也可以分别放到2台机器上,甚至多个客户端对应一个服务端(通信程序)
或者如果你是把它当dll用,也可以直接调用dll,返回一个DataTable或xml或byte数组之类的
你只是说传递如何如何,服务端如何,客户端如何,我上面说过了,不知道你看没看,我需要的是一个优化程序的组织,UI层调用同一个类或接口的方法就会根据配置参数通知这个实现调用不同处理,比如我在UI层传参“OPC”,那么就会做OPC处理,比如我在UI层传参“IO”那么就会做IO处理。这种做是可以实现的,我已经做过了,但是再往下划分的话,我就有疑问了,比如我将OPc处理的类再细划分成公共类或接口和实现类二部分,将对OPC的读写方法封装成公共的,实现部分的m1,m2,m3的实现就会依赖 这个公共部分;IO处理的也是如此,RS485的也是,也就是说同样都实现m1,m2,m3那能不能将m1,m2,m3统一成一个接口?统一成接口后调用方便了,疑问也出现 了,m1,m2,m3实现这个接口依赖如何处理?如何统一?
如果你的思路不能转变的话,简单的问题也会变得很复杂