面向对象的一点简易理解

简单面向对象套路

 1 public class Classfather
 2 {
 3     public string name { get; set; }
 4     public Classfather(string _name)
 5     {
 6         Console.WriteLine("调用了父类的构造方法");
 7         name = _name;
 8     }
 9     virtual public void fatherMethod()
10     {
11         Console.WriteLine("父类方法 myname is "+name);
12     }
13 }
14 
15 public class Classson:Classfather
16 {
17     //我们称之为接力传递
18     //父子类之间继承 构造函数 参数通过下面这种方式接力传递到父类的
19     public Classson(): base( "father")
20     {
21         Console.WriteLine("调用了子类的构造方法");
22     }
23     public Classson(string _name):base("father->"+_name)//_name接力传递
24     {
25         Console.WriteLine("调用了子类的构造方法");
26         //name = _name;
27     }
28 
29 
30     //new   会把父类的方法废掉,父类的同名方法从此与他无关 以子类为主 你可以写
31     //跟父类名字一样参数不一样的方法,相当与重载
32     //用override关键字:
33     //方法重写,父类必须要有同名方法并且带virtual关键字
34     //并且重写的时候要遵循父类同名方法的参数
35 
36     //如果在fatherMethod方法中写fatherMethod();会造成死循环 导致堆栈溢出
37     //但是如果想在Classson中访问Classfather的fatherMethod方法怎么办呢^_^
38     //用base.fatherMethod();就行啦
39     //this关键字相当于当前实例本身
40     //base关键字相当于被继承的父类
41     //在一个类中方法同名参数不同叫重载
42     //父类与子类中方法重名叫重写(并且参数也要一样^_^)
43 
44     //此方法注释后 也能调fatherMethod 只不过是父类的 ,加new关键字 则强行覆盖父类方法
45     public override void fatherMethod()
46     {
47         Console.WriteLine("子类继承的---父类的方法 myname is" + name);
48         base.fatherMethod();
49     }
50 
51    
52     public void sonNewMethod()
53     {
54         Console.WriteLine("子类的新方法----");
55     }
56 }
57 
58 public void OOPTest()
59 {
60 
61     //实例化时先执行父类构造方法,然后执行子类构造方法
62     Classfather c11 = new Classson();// ("zhangsan ");// ("c111");
63     //注意了 此处执行是执行的子类方法 引用是父类引用类型 ,最基础c++里面向对象关键所在 也就这点
64     c11.fatherMethod();
65     ((Classson)c11).sonNewMethod();
66     //输出true
67     Console.WriteLine("c11 is Classfather: " + (c11 is Classfather));
68     //输出true  由此可以提前确定类型 决定了你能否调用某函数 以免强制转换类型错误
69     //网上说叫什么协变 逆变 
70     Console.WriteLine("c11 is Classson: " + (c11 is Classson));
71 
72     //输出false
73     Classfather c22 = new Classfather("");
74     Console.WriteLine("c22 is Classson: " + (c22 is Classson));
75 
76     Console.WriteLine("-------------------------------");
77     Classson c33 = new Classson();
78     c33.fatherMethod();
79     //输出true
80     Console.WriteLine("c33 is Classson: " + (c33 is Classson));
81     //输出true
82     Console.WriteLine("c33 is Classfather: " + (c33 is Classfather));
83 
84     //fatherMethod()
85     //xxx c33 = new Classson();
86     //不管前面的引用是何类型父类还是子类 只认实例化对象 new Classson();
87     //如果子类复写了fatherMethod() c33.fatherMethod() 则调用子类,未复写则自动调用父类
88 
89     //至于能不能调用 代码提示能不能出来 那是另一回事了涉及到前面说的协变逆变 强转类型,这句切记
90     //所谓设计模式也就是玩这些个东西
91 
92     //以上充分体现了面向对象宽范围兼容窄范围的思想
93 }

两个互不相干的cls(哪怕在同一命名空间) 要想访问对方的成员 ,那个成员必须是public

如果是不同命名空间 或者项目 要想在其他的项目中 实例化那个cls,那个cls必须是public

要养成面向对象编程的习惯
到处都要用的变量 放到外面作为成员,
不要所有的业务逻辑全都一股脑写成一个个的方法堆起
不要访问级别 继承的什么都不管 全部写成 javascript函数形式的东西
这样的话跟oop就有点脱节了
可能开始不太习惯 但是你会发现这会给你设计程序带来莫大的好处

一个面向对象的例子

这些都是oop的基本常识 但是只要运用得当 也可以让你的程序运行得更好,别太在乎那些花哨的设计模式 并不是那些东西没用,只是别贸然模仿。时隔今日 又来这篇博文上添砖加瓦。上次张翔同学说不知道什么情况下新建一个类 什么情况下拓展,一时我也不知如何回答他。上周参考了以前的报价软件代码思想做出一个例子 正巧可以做说明:这里有个功能 要做各种报表导出。 然后就建一个 报表导出基类:

 1 class ReportExport
 2     {
 3         string reportName;
 4         string reportFullName;
 5         ProjectAdapter ada;
 6         //初始化模板名字 文件保存目录 和适配器
 7         public ReportExport(string _reportName, string _reportFullName)
 8         {
 9             reportName = _reportName;
10             reportFullName = _reportFullName;
11             ada = MyApp.Instance.AdapterFlyWeight.GetProjectAdapter();
12         }
13 
15         //进行数据适配
16         public virtual void AdaptiveToData()
17         {
18             dataModels = new List<BaseModel>();
19             templateNames = new List<string>();
20             sheetNames = new List<string>();
21         }
22 
23         protected List<BaseModel> dataModels;
24         protected List<string> templateNames;
25         protected List<string> sheetNames;
26         
28         //进行导出动作
29         public virtual bool Export()
30         {
31             ada.ExoprtToFile(dataModels, templateNames, sheetNames, reportFullName);
32             return true;
33         }
34     }
 然后我有个特定的名字叫甲方文件 的报表 继承这个基类
 1 class ReportJiafang:ReportExport
 2     {
 3         public ReportJiafang(string  reportJiafangFullName)
 4             : base("甲方文件报表",reportJiafangFullName)
 5         { }
 6 
 7         public override void AdaptiveToData()
 8         {
 9             base.AdaptiveToData();
11             dataModels.Add(App.Instance.CurrentProject);
12             dataModels.Add(App.Instance.CurrentProject);
13 
14             templateNames.Add("甲方文件报表汇总表");
15             templateNames.Add("甲方文件报表明细表");
16 
18             sheetNames.Add("甲方文件总表11");
19             sheetNames.Add("甲方文件明细表22");
20         }
21     }

 你看我初始化的时候 自动调用基类的构造方法 名字默认传“甲方文件报表” ,因为从类的名字上已经体现了,然后只需要传保存文件路径通过base传递给基类构造函数。然后是通过 AdaptiveToData进行数据适配处理 ,不同的报表是不一样的。所以这里需要对方法进行重写。然后调用的时候:

1 ReportJiafang rpt =new ReportJiafang("d:\\aaa.xlsx");
2 
3                     rpt.AdaptiveToData();
4                     rpt.Export();

 你看继承和面向对象编程就是有这个威力 ,对同类型的工作进行归纳放到基类处理。而在外部调用又实现了保证接口和流程的统一性。而让子类又能够拥有各自的细节化处理。你如果用面向过程的思维方式来编程 是不是要low很多 特别是处理这种问题的时候。 是不是体现了一种辩证与统一的思想。有位大佬说过,世界是电脑运行的内存 ,类是模板 不占空间, 实例化后就是具体的对象 要在现实世界占空间,接口就是技能,他有开车的技能 那么他实现接口。感觉还是很形象化的。还有某些规律跟世界的运转规律不谋而合 ,比如普创接地系统的联动控制机制 站控如果点了手动则意思代表脱离 不再受中央控制,跟web的css样式表 ,类的virtual方法重新 都是同样的理念。还有U3d里面的3d对象树结构 然后自上而下的 属性影响 数据分配 ,WPF里的数据绑定 logicTree ,virtualTree,也都是类似理念,就像这个社会自上而下的 节点分配一样。

结构设计随想

c#的项目引用 ,项目依赖 ,以前从没想过这个问题。只能上层依赖下层的 或者一个上层依赖多个下层 ,也就是我们平常在项目上做的 右键 添加引用。生成解决方案时 编译时自下而上的编译 就这么个过程。但是不能反向引用 ,下层 怎么在不引用上层的情况下 与上层交互 或者操纵上层的东西 ,也就是所谓的解耦合(把代码分到上下层不同项目就叫解耦合 没网上说的那么高深)通过最近看的一些代码包括s-manager的一些代码 ,感觉中心思想都是在围绕这个做文章。一般能想到的最基础的就是下层弄个接口叫 Ixxx  ,上层去实现 ,上层实例化下层对象的时候 关联上对应实现了接口的对象  ,这样下层对象调用的时候就不会出现调用不到,就这样就实现了控制反转 ,另外加上事件订阅 面向对象的父子类继承虚方法机制 一起撑起整个结构,虽然没见过java 所谓吹过去吹过来的所谓的IOC 也就是指这个套路吧。包括以前李思瑶写的那些,相对来说事件订阅还粗暴简单一点。不要怕把大对象放到一个地方感觉很大的样子放那感觉不是太好 其实只是个引用 根本没啥空间,集中放那比如一个固定的全局的数组里 为的就是有个丝线头在那  ,到用的时候有个抓头,如果各不同对象都继承了统一接口 ,都知道逆变协变 同一父类那到时会存的引用拿出来用的话就能够达到归一统一化,那些框架 和结构设计套路 也就是这么来的而已 ,利用了我刚刚说的这一系列面向对象特性, 并没什么神秘的。
 

一个例子

经过这么多年浸淫,看了一些设计,再怎么也还是有点长进,早就理解到c#面向对象的世界里 一个引用就是一丝线像个挂钩一样 就这样牵出来抓到调用的把柄。比如我想以窗体为基础加一个静态方法输出消息,立即就想到写个静态方法,但是通过一路c++过来的深刻理解 静态方法是没有实例的,于是很自然的想到放一个当前实例的静态变量 静态方法里间接调用。只需要注意初始化时机即可 在窗体载入时初始化 保证调用的时候有东西,这样各处都可以调用了。就这样几句简单的结构就成了:
 1 public partial class Form1 : Form
 2 {
 3     public static Form1 f1;
 4     
 5 
 6     private void Form1_Load(object sender, EventArgs e)
 7     {
 8         f1 = this;
 9 
10         //userControl11.bindEvent(this);
11     }
12 
13     public static void ShowMsg(string msg){
14         f1._ShowMsg(msg);
15     }
16 
17     private void _ShowMsg(string msg){
18         this.Invoke(new Action(()=>{
19             this.label2.Text = msg;
20         }));
21     }
22 
23     private void btn_test_Click(object sender, EventArgs e)
24     {
25         Form1.ShowMsg("showmsg test...");
26     }
27 }

又一个例子

窗体有很多子控件,当主窗体想触发某些订阅(定义了自己代码)了的控件动作时,那么就可以简单的直接使用c#自带的 事件和订阅 发布来处理,如果还想降低耦合 子控件在底层项目里怎么办呢,像s-manager里一样 底层项目定义Ixxx  主窗体实现接口 ,子控件初始化时把实现了接口的窗体自己传过去 ,子控件自己订阅并写事件自定义代码:

位于library项目的代码

 1 public interface Iform
 2 {
 3     EventHandler<MyArgs> MyEvent { get; set; }
 4 }
 5 
 6 public class MyArgs:EventArgs
 7 {
 8     public string data { get; set; }
 9 }
10 
11 public partial class UserControl1 : UserControl
12 {
13     public UserControl1()
14     {
15         InitializeComponent();
16     }
17 
18     public void bindEvent(Iform f1)
19     {
20         f1.MyEvent += new EventHandler<MyArgs>((sender, args) =>
21         {
22             label1.Text = args.data;
23         });
24     }
25     private void UserControl1_Load(object sender, EventArgs e)
26     {
27 
28     }
29 }

拖动用户控件到主项目,主窗体的代码:

 1 public partial class Form1 : Form, Iform
 2 {
 3     
 4 
 5     private void Form1_Load(object sender, EventArgs e)
 6     {
 7 
 8         userControl11.bindEvent(this);
 9     }
10     public EventHandler<MyArgs> MyEvent
11     {
12         get;
13         set;
14     }
15     private void btn_test_Click(object sender, EventArgs e)
16     {
17         MyEvent(this,new MyArgs(){ data= "userevent subscrip test..."});
18     }
19 }

 又一个例子

比如我们想让整个解决方案所有地方都调用一个叫ShowMsg的函数 起到中枢控制作用或直接展示信息,那么毫无疑问这个函数只能放在底层,但是上层有winform窗体我们想让ShowMsg能把信息展示到上层窗体,怎么做呢,还是只能在接口上做文章,上层继承接口把自己的实例传下去,这样调用 Logger.GetInstance().AppendMsg("loginfo test..."); 无论上下层 何处都可以进行日志记录。注意在开始的时候设置处理接口SetCurrentProc

library项目代码

 1 public interface Iform
 2 {
 3     void AppendMsg(string msg);
 4 }
 5 
 6 public class Logger
 7 {
 8     private static Logger loger;
 9     private Logger()
10     {
11 
12     }
13     public static Logger GetInstance()
14     {
15         if (loger == null)
16             loger = new Logger();
17         return loger;
18     }
19 
20     Iform f1;
21     public void SetCurrentProc(Iform _f1)
22     {
23         f1 = _f1;
24     }
25     public void AppendMsg(string msg){
26         if (f1 != null)
27             f1.AppendMsg(msg);
28     }
29 }

上层项目窗体代码

 1 public partial class Form1 : Form, Iform
 2 {
 3     public void AppendMsg(string msg)
 4     {
 5         this.Invoke(new Action(() => {
 6             this.label2.Text = msg;
 7         }));
 8     }
 9     private void Form1_Load(object sender, EventArgs e)
10     {
11         Logger.GetInstance().SetCurrentProc(this);
12     }
13    
14     private void btn_test_Click(object sender, EventArgs e)
15     {
16         Logger.GetInstance().AppendMsg("loginfo test...");
17     }
18 }

 

 

posted @ 2014-05-09 08:53  assassinx  阅读(388)  评论(0编辑  收藏  举报