zoukankan      html  css  js  c++  java
  • 重温面向对象核心 上

    实例解读面向对象核心,所有例子基于 C#,涉及我们实务中最常关心的问题:

    1、封装、继承、多态;

    2、抽象类、接口;

    3、委托、事件。         

    一、面向对象三大特性:封装、继承、多态

    每个对象都包含它能进行操作的所有信息(不必依赖其他对象),这个特性称为封装

    封装降低了耦合,类内部的实现可以自由的修改,使类具有清晰的对外接口。

    对象的继承代表了一种“一般到特殊”的关系,例如 学生(Student)是一种人类(Person)。

    继承定义了类如何相互关联,共享特性。

    继承的工作方式是,定义父类和子类,或叫做基类与派生类,上面的例子中,可以让 Student(子类)继承 Person(父类)。

    多态:表示不同的对象可以执行相同的动作,但要通过它们自己的实现代码执行。

    这个文字理解比较抽象,具体说明见下面示例中讲解:

    例子分为 父类,子类,调用 三部分。

    先建立个MVC项目OOPDemo,我们定义一个父类:抽象的图形类(Shape), 子类 :矩形类 (Rectangle),在 HomeController 中 Index 方法中调用。

    父类:

    定义一个属性Name 和方法 GetName, 将该成员声明为虚拟的(virtual, 有方法体),除了字段不能是虚拟的,属性、事件和索引器都可以是虚拟的,通常虚拟的是方法, 子类通过 override来覆写。

        public class Shape
    
        {
    
            public string Name { get; set; }
    
     
    
            public Shape(string name)
    
            {
    
                this.Name = name;
    
            }
    
     
    
            public virtual string GetName()
    
            {
    
                return "父类的图形名: "+ Name;
    
            }
    
        }

    子类继承父类:

        public class Rectangle:Shape
    
        {
    
            public Rectangle(string name):base(name)
    
            { }
    
     
    
            public override string GetName()
    
            {
    
                return "子类的图形名: " + Name;
    
            }
    
     
    
            public double Length { get; set; }
    
            public double Width { get; set; }
    
            public double GetArea()
    
            {
    
                double area = Length * Width;
    
                return area;
    
            }
    
        }

    子类中关于继承的说明:

    1、子类拥有父类非private的属性和功能

          子类拥有父类的 属性Name,GetName()方法。

          * 构造方法有一些特殊,它不能被继承,只能被调用(使用 base)。

            public Rectangle(string name):base(name)

            { }

    2、子类具有自己的属性和功能,即子类可以扩展父类没有的属性和功能。

          矩形有自己的 长、宽属性,及计算面积的 GetArea 方法。

            public double Length { get; set; }
    
            public double Width { get; set; }
    
            public double GetArea()
    
            {
    
                double area = Length * Width;
    
                return area;
    
            }

    3、子类可以以自己的方式实现父类的功能(方法重写)

         通过override重写

            public override string GetName()
    
            {
    
                return "子类的图形名: " + Name;
    
            }

    调用

    在HomeController的Index方法中获取名字。

            public IActionResult Index()
    
            {
    
                Shape rec1 = new Rectangle("正方形");
    
                ViewBag.Name = rec1.GetName();
    
                return View();
    
            }

    前端通过ViewBag获取Name

     

    调用时关于多态的说明

    1、子类以父类身份出现

          注意是以 Shape(父类) 而不是 Rectangle(子类) 来声明的,然后用 Rectangle(子类)来实例化。(对象的声明是父类,而不是子类,实例化的对象是子类)

               

     Shape rec1 = new Rectangle("正方形");

    2、子类在工作时以自己的方式来实现(覆写父类方法)

            public override string GetName()
    
            {
    
                return "子类的图形名: " + Name;
    
            }

    当方法被调用时,都只有位于对象继承链最末端的方法会被调用。也就是说,虚方法是按照运行时类型而非编译时类型进行动态绑定调用的。

    3、子类以父类身份出现时,子类特有的属性和方法不可使用

    这些都是不能用的:

            public double Length { get; set; }
    
            public double Width { get; set; }
    
            public double GetArea()
    
            {
    
                double area = Length * Width;
    
                return area;
    
            }

    例如 rec1.GetArea() ,这个是获取不到的。

    二、抽象类与接口

    抽象类

    回顾下我们的例子。

    Shape实际上不会实例化,它只是抽象出一些共同的东西用来继承。

    抽象类通常代表一个抽象概念,它提供一个继承的出发点,当设计一个新的抽象类时,一定是用来继承的,所以,在一个以继承关系形成的等级结构里面,树叶节点应当是具体类,而树枝节点应该是抽象类。也就是说,具体类不是用来继承的。

    考虑把没有任何意义的父类改成抽象类,让抽象类拥有尽可能多的共同代码,拥有尽可能少的数据。

    我们来修改一下例子。

    将父类做如下方框处更改,其他的都不变。

     

    我们将Shape改成了抽象类, public abstract class Shape {…}

    将GetName删除了方法体,改成了抽象方法 public abstract string GetName();

    说明:

    1、抽象类不能实例化

    2、抽象方法是必须被子类重写的方法(可以看成是没有实现体的虚方法)

    3、如果类中包含抽象方法,那么类就必须被定义为抽象类,不论是否还包含其他一般方法。

    接口

    声明接口的语法与声明抽象类完全相同,但不允许提供接口中任何成员的执行方法。

    实现接口的类就必须实现接口中所有的方法和属性。

    我们来定义一个接口:

        /// <summary>
    
        /// 定义各种各样的面积算法
    
        /// </summary>
    
        public interface ICal
    
        {
    
            string GetAreaAlgorithm();
    
        }
    

      

    在Rectangle中继承这个接口,并实现接口的方法

        public class Rectangle:Shape, ICal
    
        {
    
     
    
            // 此处省略其他代码xx行...
    
            // 实现接口 ICal 中的方法
    
            public string GetAreaAlgorithm()
    
            {
    
                return "矩形的面积算法:长 × 宽";
    
            }
    
    }

    修改调用方法:

            public IActionResult Index()
    
            {
    
                Shape rec1 = new Rectangle("正方形");
    
                ViewBag.Name = rec1.GetName();
    
     
    
                ICal cal=  new Rectangle("正方形2");
    
                ViewBag.Cal = cal.GetAreaAlgorithm();
    
     
    
                return View();
    
            }

    显示结果:

     

    三、总结:

    1、类是对对象的抽象;抽象类是对类的抽象;接口是对行为的抽象

    2、如果行为跨越不同对象,可使用接口

    3、从设计角度看,抽象类是从子类中发现公共的东西,泛化出父类,而接口根本不知道子类的存在,方法如何实现还不确认,预先定义。

    抽象类往往都是通过重构得来的,抽象类是自底向上抽象出来的,而接口是自顶向下设计出来的。

    祝学习进步 :)

  • 相关阅读:
    Linux内核配置过程
    Linux内核最顶层文档
    LeetCode 11月第2周题目汇总
    Chapter0
    序列加法的讨论
    ch2-基本工具介绍
    ch1-数据科学概述
    在Linux下制作Linux&windows启动盘
    VMware Workstation 与 Device/Credential Guard 不兼容?
    Linux mint 19.3配置CUDA+安装Tensorflow
  • 原文地址:https://www.cnblogs.com/miro/p/11306899.html
Copyright © 2011-2022 走看看