【大话设计模式】附录A:面向对象(1/30)

1.对象

  • 对象是一个自包含的实体,用一组可识别的特性和行为来标识。
  • 通俗讲一切皆为对象,即所有东西都是对象,对象就是可以看到、感觉到、听到、摸到、尝到、或闻到的东西。

2.类

  • 类就是具有相同的属性和功能的对象的抽象的集合。
  • 1.类名首字母都大写。2.对外公开的方法用public。
  • 实例化:实例就是一个真实的对象。实例化就是创建对象的过程,使用new关键字创建。

3.构造方法

  • 又叫构造函数,其实就是对类进行初始化。构造方法与类同名,无返回值,也不需要void,在new的时候调用。
  • 所有类都有构造方法,自动生成空的,若定义默认失效。

4.方法重载

  • 提供了创建同名的多个方法,可使用不同的参数类型。

5.属性

  • 属性是一个方法或一对方法,但在调用他的代码来看,他是一个字段,即属性适合于以字段的方式使用方法调用的场合。(字段是存贮类要满足其设计所需要的的数据,字段是与类相关的变量)。
  • 通常字段都是private,而属性都是public。
  • 私有字段通常第一个首字母小写(或前面加_),公有属性所有首字母大写。

6.get和set

  • 属性有两个方法get和 set。
  • get 访问器返回与声明的属性相同的数据类型,表示的意思是调用时可以得到内部字段的值或引用;
  • set访问器没有显示设置参数,但它有一个隐式参数,用关键字value表示,它的作用是调用属性时可以给内部的字段或引用赋值。

7.封装

  • 每个对象都包含它能进行操作所需要的的所有信息,这个特性称为封装,因此对象不必依赖其他对象来完成自己的操作。
  • 好处1:良好的封装能减少耦合。
  • 好处2:类内部的实现可以自由地修改。
  • 好处3:类具有清晰的对外接口。

8.继承

  • 当两个类之间具备‘is-a’的关系时,就可以考虑用继承了。
  • 当两个类之间是‘has-a’的关系时,表示某个角色具有某一项责任,此时不合适用继承。比如人有两只手,手不能继承人;再比如飞机场有飞机,这飞机也不能去继承飞机场。
  • 继承者可以理解为是对被继承者的特殊化,因为除了具备继承者的特性外,还具备自己独有的个性。
  • 继承定义了类如何相互关联,共享特性。继承的工作方式是,定义父类和子类,或叫做基类和派生类,其中子类继承父类的所有特性。子类不但继承了父类的所有特性,还可以定义新的特性。
  • 子类继承父类,则子类就拥有父类的除private外的属性和功能。
  • 子类从他的父类中继承的成员有方法、域、属性、事件、索引指示器,但对于构造方法,有一些特殊,他不能被继承,只能被调用。对于调用父类的成员,可以用base关键字。
  • 不用继承的话,如果要修改功能,就必须在所有重复的方法中修改,代码越多,出错的可能就越大,而继承的优点是,继承使得所有子类公共的部分都放在了父类,使得代码得到了共享,这就避免了重复,另外,继承可使得修改或扩展继承而来的实现都较为容易。”

9.多态

  • 多态表示不同的对象可以执行相同的动作,但要通过他们自己的实现代码来执行。
  • 举例:我来举个例子你就好理解了。我们的国粹‘京剧’以前都是子承父业,代代相传的艺术。假设有这样一对父子,父亲是非常有名的京剧艺术家,儿子长大成人,模仿父亲的戏也惟妙惟肖。有一天,父亲突然发高烧,上不了台表演,而票都早就卖出,退票显然会大大影响声誉。怎么办呢?由于京戏都是需要化妆才可以上台的,于是就决定让儿子代父亲上台表演。”
    “化妆后谁认识谁呀,只要唱得好就可以糊弄过去了。”
    “是呀,这里面有几点注意,第一,子类以父类的身份出现,儿子代表老子表演,化妆后就是以父亲身份出现了。第二、子类在工作时以自己的方式来实现,儿子模仿得再好,那也是模仿,儿子只能用自己理解的表现方式去模仿父亲的作品;第三、子类以父类的身份出现时,子类特有的属性和方法不可以使用,儿子经过多年学习,其实已经有了自己的创作,自己的绝活,但在此时,代表父亲表演时,绝活是不能表现出来的。当然,如果父亲还有别的儿子会表演,也可以在此时代表父亲上场,道理也是一样的。这就是多态。”
  • 第一,子类以父类的身份出现。第二、子类在工作时以自己的方式来实现。第三、子类以父类的身份出现时,子类特有的属性和方法不可以使用。
  • 此处需要了解虚方法和方法重写。
    • 为了使子类的实例完全结题来自父类的类成员,父类必须将该成员声明为虚拟的。
    • 声明方法是通过在该成员的返回类型之前添加virtual关键字来实现。
    • 通常虚拟的是方法,但其实除了字段不能是虚拟的,属性、事件和索引器都可以是虚拟的。
    • 尽管方法可以是虚拟的,但虚方法还是有方法体,可以实际做些事情。
    • 然后,子类可以选择使用override关键字,将父类实现替换为它自己的实现,这就是方法重写Override,或者叫做方法覆写。
  • 不过一定要注意了,这个对象的声明必须是父类,而不是子类,实例化的对象是子类,这才能实现多态。
  • 多态的原理是当方法被调用时,无论对象是否被转换为其父类,都只有位于对象继承链最末端的方法实现会被调用。也就是说,虚方法是按照其运行时类型而非编译时类型进行动态绑定调用的。

10.重构

  • 不同类除了构造方法,还有重复,可以使用重构解决。
  • 通过重构改善既有代码的设计,体现了敏捷开发的思想。

重构前

重构Animal类后

调用

11.抽象类

  • 把实例化没有任何意义的父类,可以改成抽象类,同样对于没有意义的方法,也可以改为抽象方法。使用关键字abstract。
  • 抽象类需要注意几点,第一,抽象类不能实例化。第二,抽象方法是必须被子类重写的方法,不重写的话,它的存在又有什么意义呢?其实抽象方法可以被看成是没有实现体的虚方法。第三,如果类中包含抽象方法,那么类就必须定义为抽象类,不论是否还包含其他一般方法。
  • 抽象类拥有尽可能多的共同代码,拥有尽可能少的数据。
  • 什么时候用:抽象类通常代表一个抽象概念,它提供一个继承的出发点,当设计一个新的抽象类时,一定是用来继承的,所以,在一个以继承关系形成的等级结构里面,树叶节点应当是具体类,而树枝节点均应当是抽象类。也就是说,具体类不是用来继承的。我们作为编程设计者,应该要努力做到这一点。比如,若猫、狗、牛、羊是最后一级,那么它们就是具体类,但如果还有更下面一级的金丝猫继承于猫、哈巴狗继承于狗,就需要考虑把猫和狗改成抽象类了,当然这也是需要具体情况具体分析的。

12.接口

  • 接口 interface。接口是把隐式公共方法和属性组合起来,以封装特定功能的一个集合。一旦类实现了接口,类就可以支持接口所指定的所有属性和成员。声明接口在语法上与声明抽象类完全相同,但不允许提供接口中任何成员的执行方式。所以接口不能实例化,不能有构造方法和字段;不能有修饰符,比如 public、private等;不能声明虚拟的或静态的等。还有实现接口的类就必须要实现接口中的所有方法和属性。
  • 一个类可以支持多个接口,多个类也可以支持相同的接口。
  • 接口的命名,前面要加一个大写字母“I”,这是规范。

13.抽象类与接口的区别

  • 从表象上来说
    • 抽象类可以给出一些成员的实现,接口却不包含成员的实现
    • 抽象类的抽象成员可被子类部分实现,接口的成员需要实现类完全实现,一个类只能继承一个抽象类,但可实现多个接口等等。
  • 重要的三点能帮助我们去区分抽象类和接口的。
    • 第一,类是对对象的抽象;抽象类是对类的抽象:接口是对行为的抽象。接口是对类的局部(行为)进行的抽象,而抽象类是对类整体(字段、属性、方法〉的抽象。如果只关注行为抽象,那么也可以认为接口就是抽象类。总之,不论是接口、抽象类、类甚至对象,都是在不同层次、不同角度进行抽象的结果,它们的共性就是抽象。
    • 第二,如果行为跨越不同类的对象,可使用接口;对于一些相似的类对象,用继承抽象类。比如猫呀狗呀它们其实都是动物,它们之间有很多相似的地方,所以我们应该让它们去继承动物这个抽象类,而飞机、麻雀、超人是完全不相关的类,叮仍是动漫角色,孙悟空是古代神话人物,这也是不相关的类,但它们又是有共同点的,前三个都会飞,而后两个都会变出东西,所以此时让它们去实现相同的接口来达到我们的设计目的就很合适了。”
    • 第三,从设计角度讲,抽象类是从子类中发现了公共的东西,泛化出父类,然后子类继承父类,而接口是根本不知子类的存在,方法如何实现还不确认,预先定义。
  • 其实实现接口和继承抽象类并不冲突的,我完全可以让超人继承人类,再实现飞行接口。
  • 通常抽象类是自底而上抽象出来的,而接口则是自顶向下设计出来的。

14.集合

  • 数组优缺点:【优点】比如说数组在内存中连续存储,因此可以快速而容易地从头到尾遍历元素,可以快速修改元素等等。【缺点】应该是创建时必须要指定数组变量的大小,还有在两个元素之间添加元素也比较困难。
  • 集合:数组长度设置过大或过小都不对,所以.Net framework提供了用于数据存贮和检索的专用类,这些类统称为集合。这些类提供对堆栈、队列、列表和哈希表的支持。大多数集合类实现相同的接口。
  • 集合可以接受任何object类型的元素。
  • 最常用的集合类ArrayList。
    • 首先 ArrayList 是命名空间System.Collections 下的一部分,它是使用大小可按需动态增加的数组实现IList接口。
    • ArrayList的容量是ArrayList可以保存的元素数。ArrayList 的默认初始容量为0。随着元.素添加到ArrayList 中,容量会根据需要通过重新分配自动增加。使用整数索引可以访问此集合中的元素。此集合中的索引从零开始。
    • 优点:集合可以根据大小按需动态增加,随意实现添加、插入、移除某一范围元素,比数组要方便。
    • 缺点:
      • 不是类型安全的,因为在他眼里所有元素都是object类型的。
      • 用ArrayList就意味着都需要将值类型装箱为Object对象,使用集合元素时,还需要执行拆箱操作,这就带来了很大的性能损耗。
  • 装箱与拆箱
    • 装箱:就是把值类型打包到Object引用类型的一个实例中。
    • int i = 123;
      object o = (object) i;  // 装箱
    • 拆箱:就是指从对象中提取值类型。
    • o = 123;
      i = (int) o;  // 拆箱
    • 相对于简单的赋值而言,装箱和拆箱过程需要进行大量的计算。
    • 对值类型进行装箱时,必须分配并构造一个全新的对象。
    • 其次,拆箱所需的强制转换也需要进行大量的计算。
    • 总之,装箱拆箱是耗资源和时间的。而ArrayList集合在使用值类型数据时,其实就是在不断地做装箱和拆箱的工作,这显然是非常糟糕的事情。
    • C# 2.0版出来后,基本不使用ArrayList了,提供了新的技术来解决这个问题,那就是泛型。

15.泛型

  • 泛型是具有占位符(类型参数〉的类、结构、接口和方法,这些占位符是类、结构、接口和方法所存储或使用的一个或多个类型的占位符。泛型集合类可以将类型参数用作它所存储的对象的类型的占位符;类型参数作为其字段的类型和其方法的参数类型出现。
  • 命名空间:System.Collections.Generic。
  • 通常使用IList<T> virName 或 List<T> virName。
  • 通常情况下,都建议使用泛型集合,因为这样可以获得类型安全的直接优点而不需要从基集合类型派生并实现类型特定的成员。此外,如果集合元素为值类型,泛型集合类型的性能通常优于对应的非泛型集合类型(并优于从非泛型基集合类型派生的类型),因为使用泛型时不必对元素进行装箱。

16.委托与事件

  • 委托是对函数的封装,可以当作给方法的特征指定一个名称。而事件则是委托的一种特殊形式,当发生有意义的事情时,事件对象处理通知过程。
  • 在一个类中要关联另一个类时使用,所以在Cat类当中,是不应该关联Mouse类的。此时用委托事件的方式就是最好的处理办法了。
  • 注意,委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。
  • 委托对象用关键字delegate来声明。而事件是说在发生其他类或对象关注的事情时,类或对象可通过事件通知它们[MSDN]。
  • 事件对象用event关键字声明。
  • // 关键代码
    // 声明一个委托,名称为CatShoutEventHandler,无参数,无返回值。
    public delegate void CatShoutEventHandler();   
    
    // 声明一个对外公开的public事件CatShout,他的事件类型是委托。表明事件发生时,执行被委托的方法。
    public event CatShoutEventHandler CatShout;
    
    
    // Cat类实际代码
    class Cat
    {
        private string name;
        public Cat (string name)
        {
            this.name = name;
        }
    
        // 声明委托CatShoutEventHandler
        public delegate void CatShoutEventHandler();  
    
        // 声明事件CatShout,类型是委托CatShoutEventHandler
        public event CatShoutEventHandler CatShout;  
    
        public void Shout()
        {
            console.WriteLine("喵,我是{0}.", name);
            if (CatShout != null)
            {
                // 当执行Shout()方法时,如果CatShout中有对象登记事件,则执行CatShout()
                CatShout();  
            }
        }
    
    }
    
    
    // Mouse类实际代码
    class Mouse
    {
        private string name;
        public Mouse(string name)
        {
            this.name = name;
        }
        
        public void Run()
        {
            console.WirteLine("老猫来了,{0}快跑!", name);
        }
    }
    
    
    // Main函数的实际代码
    static void Main(string[] args)
    {
        // 实例化老猫Tom以及小老鼠
        Cat cat = new Cat("Tom");
        Mouse mouse1 = new Mouse("Jerry");
        Mouse mouse2 = new Mouse("Jack");
    
        // 将Mouse的Run方法通过实例化委托Cat.CatShoutEventHandler登记到Cat的事件CatShout当中。其中“+=”表示“add_CatShout”的意思
        cat.CatShout += new Cat.CatShoutEventHandler(mouse1.Run);
        cat.CatShout += new Cat.CatShoutEventHandler(mouse2.Run);
    
        cat.Shout();
        Console.Read();
    }
  • +=:表示增加委托实例对象的意思,相当于add_CatShout()。
  • -=:移除委托实例的操作,相当于remove_CatShout()。

17.sender和e

  • 以private void buttonl_Click(object sender, EventArgs e)为例。
  • 原来object sender就是传递发送通知的对象,而EventArgs是包含事件数据的类。
  • EventArgs e
    • 是包含事件数据的类的基类
    • 换句话说,这个类的作用就是用来在事件触发时传递数据用的。
  • Object sender
    • 传递发送通知的对象。

18.修饰符

  • 访问修饰符
    • private:私有的,只能在当前类内部使用。
    • public:可以在当前类及其他类使用。
    • protected:定义的父类和子类内可使用。继承时子类可以对基类有完全访问权。也就是父类对子类公开,但不对其他类公开。
    • internal:是类型和类型成员的访问修饰符。只有在同一程序集的文件中,内部类型或成员才是可访问的。
  • 类修饰符
    • abstract:指示某个类只能是其他类的基类。可以和类、方法、属性、索引器及事件一起使用。在类声明中使用abstract修饰符以指示某个类只能是其他类的基类。标记为抽象或包含在抽象类中的成员必须通过从抽象类派生的类来实现。
    • sealed:密封类指定类不能被继承。可以应用于类、实例方法和属性。密封类不能被继承。密封方法会重写基类中的方法,但其本身不能在任何派生类中进一步重写。当应用于方法或属性时,sealed修饰符必须始终与override一起使用。在类声明中使用sealed修饰符可防止继承此类。
    • static:声明属于类型本身而不是属于特定对象的成员。声明属于类型本身而不是属于特定对象的静态成员。可用于类、字段、方法、属性、运算符、事件和构造函数,但不能用于索引器、析构函数或类以外的类型。
    • partial:在整个同一程序集中定义分部类和结构。分部类型定义允许将类、结构或接口的定义拆分到多个文件中。
  • 方法或变量修饰符
    • const:指定无法修改字段或局部变量的值。它指定字段或局部变量的值是常数,不能被修改。
    • new:从基类成员隐藏继承的成员。在用作修饰符时,new关键字可以显式隐藏从基类继承的成员。隐藏继承的成员意味着该成员的派生版本将替换基类版本。在不使用new修饰符的情况下隐藏成员是允许的,但会生成警告。使用new显式隐藏成员会取消此警告,并记录代之以派生版本这一事实。若要隐藏继承的成员,请使用相同名称在派生类中声明该成员,并使用new修饰符修饰该成员。对同一成员同时使用new和override是错误的,因为这两个修饰符在含义上相互排斥。使用new会用同样的名称创建一个新成员并使原始成员变为隐藏的,而override则扩展继承成员的实现。
    • virtual:在派生类中声明其实现可由重写成员更改的方法或访问器。用于修饰方法、属性、索引器或事件声明,并且允许在派生类中重写这些对象。调用虚方法时,将为重写成员检查该对象的运行时类型。将调用大部分派生类中的该重写成员,如果没有派生类重写该成员,则它可能是原始成员。默认情况下,方法是非虚拟的。不能重写非虚方法。virtual修饰符不能与static、abstract和override修饰符─起使用。除了声明和调用语法不同外,虚拟属性的行为与抽象方法—样。
    • override:提供从基类继承的虚拟成员的新实现。要扩展或修改继承的方法、属性、索引器或事件的抽象实现或虚实现,必须使用override修饰符。
    • readonly:声明一个字段,该字段只能赋值为该声明的一部分或者在同一个类的构造函数中。是可以在字段上使用的修饰符。当字段声明包括readonly修饰符时,该声明引入的字段赋值只能作为声明的一部分出现,或者出现在同一类的构造函数中。
  • 特殊修饰符
    • event:声明事件。类和结构使用事件将出现的可能影响对象状态的事件通知给对象。
    • extern:指示在外部实现方法。用于声明在外部实现的方法。extern修饰符的常见用法是在使用Interop服务调入非托管代码时与Dlllmport属性—起使用;在这种情况下,该方法还必须声明为static。extern关键字还可以定义外部程序集别名,使得可以从单个程序集中引用同一组件的不同版本。将abstract和extern修饰符一起使用来修改同一成员是错误的。使用extern修饰符意味着方法在C#代码的外部实现,而使用abstract修饰符意味着在类中未提供方法实现。
    • unsafe:声明不安全的上下文。该上下文是任何涉及指针的操作所必需的。
    • volatile:指示字段可由操作系统、硬件或并发执行线程等在程序中进行修改。volatile关键字表示字段可能被多个并发执行线程修改。声明为volatile的字段不受编译器优化(假定由单个线程访问)的限制。这样可以确保该字段在任何时间呈现的都是最新的值。

声明:本文为学习过程中的记录整理,分享出来以便初学者能够有所参考,无法保证完全正确性,大神请绕道,学习者请自行斟酌。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值