C#知识点-10(多态:虚方法、抽象类、接口,单例设计模式、简单工厂设计模式、依赖注入)

多态

定义:让一个对象能够表现出多种的状态(类型)
实现多态的三种手段:虚方法、抽象类、接口

虚方法(virtual):

将父类的方法标记为virtual,表示是一个虚方法。将子类的方法标记为override,表示重写父类的方法,而不是隐藏父类的方法

鸭子案例:

真实的鸭子嘎嘎叫,木头鸭子吱吱叫,橡皮鸭子叽叽叫

    internal class Program
    {
        static void Main(string[] args)
        {
            RealDuck duck = new MTDuck();
            duck.Bark(); //木头鸭子吱吱叫
            Console.ReadKey();
        }
    }
    class RealDuck
    {
        public virtual void Bark()
        {
            Console.WriteLine("真实的鸭子嘎嘎叫");
        }
    }
    class MTDuck:RealDuck
    {
        public override void Bark()
        {
            Console.WriteLine("木头鸭子吱吱叫");
        }
    }
    class XPDuck:RealDuck
    {
        public override void Bark()
        {
            Console.WriteLine("橡皮鸭子叽叽叫");
        }
    }

当我们的一个父类,指向了一个子类后。正常情况下父类只能调用到自己的成员,因为表现出来就是父类自己的类型。但如果在父类的方法前加了virtual关键字,并且在子类方法前加了override关键字,还有一点就是父类与子类的方法名必须一样。这样的话当我们调用父类的方法的时候,就会判断,父类在内存中指向了哪一个子类对象。指向了哪一个子类对象,就执行那个子类对象override的方法

抽象类(abstract)

定义:抽象类是一种不能被实例化的类,其目的是为了被其它类继承并实现其抽象方法。

案例:计算圆形、矩形的面积和周长
我们先定义一个父类Shape,这个问题的共同点在于都要计算图形的面积和周长。在父类中写两个方法,一个计算面积,一个计算周长。但是我们没办法在父类中写具体的代码,因为面积周长的计算方式因图形不同而异。所以我们只在父类中写方法而不写具体的方法体,但是这样是“不合法”的。这时候我们将方法定义为抽象方法,添加一个abstract关键字。而抽象方法所在的类也必须是抽象类,所以我们在父类中也加入abstract关键字。

    internal class Program
    {
        static void Main(string[] args)
        {
            Shape s = new Circle(5);
            double area = s.GetArea();
            double zhouchang = s.GetZhouChang();
            Console.WriteLine(area);
            Console.WriteLine(zhouchang);
            Console.ReadKey();
        }
    }
    abstract class Shape
    {
        public abstract double GetArea();//面积
        public abstract double GetZhouChang();//周长
    }
    class Circle : Shape   //圆形
    {
        public double R { get; set; }
        public Circle(double r)
        {
            this.R = r;
        }

        public override double GetArea()
        {
            return Math.PI * this.R * this.R;
        }

        public override double GetZhouChang()
        {
            return 2 * Math.PI * this.R;
        }
    }
    class Rectangle : Shape   //矩形
    {
        public double Height { get; set; }
        public double Width { get; set; }
        public Rectangle(double height, double width)
        {
            this.Height = height;
            this.Width = width;
        }

        public override double GetArea()
        {
            return this.Height * this.Width;
        }

        public override double GetZhouChang()
        {
            return 2 * (this.Height + this.Width);
        }
    }

抽象类总结:
1、抽象成员必须被标记为abstract
2、抽象成员必须在抽象类中
3、抽象类不能被实例化
4、子类继承抽象类后,必须把抽象类的所有抽象成员重写(除非子类也是一个抽象类)
5、抽象成员的访问修饰符不能是private,并且抽象类的实例成员可以不被子类重写
6、抽象类是有构造函数的,虽然不能被实例化
7、如果抽象类的抽象方法有参数,那么子类在重写的时候必须也传入相应的参数(返回值)
8、如果父类中的方法有默认实现,并且父类需要被实例化,这时可以考虑将父类定义成普通类,用虚方法来实现
9、如果父类的方法没有默认实现,并且可以不被实例化,则可以将父类定义成抽象方法

单例设计模式

设计模式:为了解决某一类固定的问题
单例设计模式:保证一个类,只有一个实例
 

  //懒汉式单例设计模式(用的时候才创建对象)
    internal class Program
    {
        static void Main(string[] args)
        {
            Person.GetSinglePerson();
        }
    }
    class Person
    {
        //1、构造函数私有化
        private Person() { }
        //2、声明一个静态的字段,来存储唯一的对象
        public static Person _singlePerson = null;
        //3、提供一个静态方法,用来返回全局唯一的对象
        public static Person GetSinglePerson()
        {
            if (_singlePerson == null)
            {
                _singlePerson = new Person();
            }
            return _singlePerson;
        }
    }

懒汉式单例设计模式在多线程时会有安全隐患 

  //饿汉式设计模式
    internal class Program
    {
        static void Main(string[] args)
        {
            Person.GetSinglePerson();
        }
    }
    class Person
    {
        private Person() { }
        //程序运行之前,这个对象就已经创建了,并且整个程序运行期间这个对象只创建一次
        private static Person _singlePerson = new Person();
        public static Person GetSinglePerson()
        {
            return _singlePerson;
        }
    }

简单工程设计模式

定义:让我们的程序,根据用户的需求,返回特定的对象

选电脑案例,根据品牌选择电脑

    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("请输入你想要的笔记本");
            string brand = Console.ReadLine();
            Computer computer = ComputerFactory(brand);
            if (computer == null)
            {
                Console.WriteLine("没有你要的品牌");
            }
            else
            {
                computer.SayHello();
            }
            Console.ReadKey();
        }
        static Computer ComputerFactory(string input)//电脑工厂
        {
            Computer computer = null;
            switch (input)
            {
                case "联想":
                    computer = new Lenovo("联想");
                    break;
                case "戴尔":
                    computer = new Dell("戴尔");
                    break;
                case "宏碁":
                    computer = new Acer("宏碁");
                    break;
                case "惠普":
                    computer = new HP("惠普");
                    break;
            }
            return computer;
        }
    }
    abstract class Computer
    {
        public string Brand { get; set; }
        public Computer(string brand)
        {
            this.Brand = brand;
        }
        public abstract void SayHello();
    }
    class Lenovo : Computer
    {
        public Lenovo(string brand) : base(brand)
        {
        }
        public override void SayHello()
        {
            Console.WriteLine("我是联想笔记本");
        }
    }
    class Dell : Computer
    {
        public Dell(string brand) : base(brand)
        {
        }
        public override void SayHello()
        {
            Console.WriteLine("我是戴尔笔记本");
        }
    }
    class Acer : Computer
    {
        public Acer(string brand) : base(brand)
        {
        }
        public override void SayHello()
        {
            Console.WriteLine("我是宏碁笔记本");
        }
    }
    class HP : Computer
    {
        public HP(string brand) : base(brand)
        {
        }
        public override void SayHello()
        {
            Console.WriteLine("我是惠普笔记本");
        }
    }

 

 打折案例,用户输入打折方式,根据选择不同计算方法不同
 

    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("请选择打折方式 1--500减50 2--300减40 3--八折 4--九折 5--不打折");
            string str = Console.ReadLine();
            ZheKou z = GetZheKou(str);
            if (z == null)
            {
                Console.WriteLine("没有你选择的打折方式");
            }
            else
            {
                double b = z.ChooseZheKou(2000);
                Console.WriteLine(b);
            }
            Console.ReadKey();

        }
        static ZheKou GetZheKou(string i)
        {
            ZheKou z = null;
            switch (i)
            {
                case "1":
                    z = new MaiMSongN(500, 50);
                    break;
                case "2":
                    z = new MaiMSongN(300, 40);
                    break;
                case "3":
                    z = new DaNZhe(8);
                    break;
                case "4":
                    z = new DaNZhe(9);
                    break;
                case "5":
                    z = new YuanJia();
                    break;
            }
            return z;
        }
    }
    abstract class ZheKou
    {
        public abstract double ChooseZheKou(double i);
    }
    class MaiMSongN : ZheKou
    {
        public MaiMSongN(double m, double n)
        {
            this.M = m;
            this.N = n;
        }
        public double M { get; set; }
        public double N { get; set; }

        public override double ChooseZheKou(double i)
        {
            if (i > this.M)
            {
                return i - (i / this.M) * this.N;
            }
            else
            {
                return i;
            }
        }
    }

    class DaNZhe : ZheKou
    {
        public DaNZhe(double n)
        {
            this.N = n;
        }

        public double N { get; set; }

        public override double ChooseZheKou(double i)
        {
            return i * this.N * 0.1;
        }
    }
    class YuanJia : ZheKou
    {
        public override double ChooseZheKou(double i)
        {
            return i;
        }
    }

依赖注入

定义:让一个类与另一个类产生联系
依赖注入三种方式:1、通过属性注入
                                2、通过构造函数注入
                                3、通过方法的参数注入
案例:模拟 把移动硬盘等插到电脑上读写数据

//1、通过属性注入
    internal class Program
    {
        static void Main(string[] args)
        {
            //模拟把移动硬盘/U盘/MP3插到电脑上,读写数据
            Computer cpu = new Computer();
            cpu.MF = new MobileDisk();
            cpu.Read();
            cpu.Write();
            Console.ReadKey();
        }
    }
    class Computer
    {
        //调用插到掉闹上的移动设备的读数据的方法和写数据的方法
        //1、属性注入
        public MobileFather MF { get; set; }
        //2、提供一个读数据的方法
        public void Read()
        {
            this.MF.Read();
        }
        //3、提供一个写数据的方法
        public void Write()
        {
            this.MF.Write();
        }
    }
    abstract class MobileFather
    {
        public abstract void Read();
        public abstract void Write();
    }
    class UDisk : MobileFather
    {
        public override void Read()
        {
            Console.WriteLine("优盘读取数据");
        }

        public override void Write()
        {
            Console.WriteLine("优盘写入数据");
        }
    }
    class MobileDisk : MobileFather
    {
        public override void Read()
        {
            Console.WriteLine("移动硬盘读取数据");
        }

        public override void Write()
        {
            Console.WriteLine("移动硬盘写入数据");
        }
    }
    class MP3 : MobileFather
    {
        public override void Read()
        {
            Console.WriteLine("MP3盘读取数据");
        }

        public override void Write()
        {
            Console.WriteLine("MP3写入数据");
        }
        public void Music()
        {
            Console.WriteLine("MP3听音乐");
        }
    }
//2、通过构造函数注入
    internal class Program
    {
        static void Main(string[] args)
        {
            //模拟把移动硬盘/U盘/MP3插到电脑上,读写数据
            Computer cpu = new Computer(new MobileDisk());
            cpu.Read();
            cpu.Write();
            Console.ReadKey();
        }
    }
    class Computer
    {
        public MobileFather MF { get; set; }
        //2、提供一个读数据的方法
        public void Read()
        {
            this.MF.Read();
        }
        //3、提供一个写数据的方法
        public void Write()
        {
            this.MF.Write();
        }
        public Computer(MobileFather mF)
        {
            this.MF = mF;
        }
    }
    abstract class MobileFather
    {
        public abstract void Read();
        public abstract void Write();
    }
    class UDisk : MobileFather
    {
        public override void Read()
        {
            Console.WriteLine("优盘读取数据");
        }

        public override void Write()
        {
            Console.WriteLine("优盘写入数据");
        }
    }
    class MobileDisk : MobileFather
    {
        public override void Read()
        {
            Console.WriteLine("移动硬盘读取数据");
        }

        public override void Write()
        {
            Console.WriteLine("移动硬盘写入数据");
        }
    }
    class MP3 : MobileFather
    {
        public override void Read()
        {
            Console.WriteLine("MP3盘读取数据");
        }

        public override void Write()
        {
            Console.WriteLine("MP3写入数据");
        }
        public void Music()
        {
            Console.WriteLine("MP3听音乐");
        }
    }

//3、通过方法注入
    internal class Program
    {
        static void Main(string[] args)
        {
            //模拟把移动硬盘/U盘/MP3插到电脑上,读写数据
            Computer cpu = new Computer();
            MobileDisk md = new MobileDisk();
            cpu.Read(md);
            cpu.Write(md);
            Console.ReadKey();
        }
    }
    class Computer
    {
        public void Read(MobileFather mf)
        {
            mf.Read();
        }
        public void Write(MobileFather mf)
        {
            mf.Write();
        }
    }
    abstract class MobileFather
    {
        public abstract void Read();
        public abstract void Write();
    }
    class UDisk : MobileFather
    {
        public override void Read()
        {
            Console.WriteLine("优盘读取数据");
        }

        public override void Write()
        {
            Console.WriteLine("优盘写入数据");
        }
    }
    class MobileDisk : MobileFather
    {
        public override void Read()
        {
            Console.WriteLine("移动硬盘读取数据");
        }

        public override void Write()
        {
            Console.WriteLine("移动硬盘写入数据");
        }
    }
    class MP3 : MobileFather
    {
        public override void Read()
        {
            Console.WriteLine("MP3盘读取数据");
        }

        public override void Write()
        {
            Console.WriteLine("MP3写入数据");
        }
        public void Music()
        {
            Console.WriteLine("MP3听音乐");
        }
    }

依赖注入案例:模拟电脑店卖电脑
以下是最终要实现的结果图

先写一个电脑类(Computer)用于存储所有电脑的父类
 

    internal class Computer
    {
        public string Brand { get; set; }
        public double Price { get; set; }
        public string ID { get; set; }
        public Computer(string brand, double price, string iD)
        {
            this.Brand = brand;
            this.Price = price;
            this.ID = iD;
        }
    }

然后我们顺便将四个不同品牌电脑的类写了

Lenovo.cs

    internal class Lenovo : Computer
    {
        public Lenovo(string brand, double price, string iD) : base(brand, price, iD)
        {
        }
    }

HP.cs

    internal class HP : Computer
    {
        public HP(string brand, double price, string iD) : base(brand, price, iD)
        {
        }
    }

Dell.cs

    internal class Dell : Computer
    {
        public Dell(string brand, double price, string iD) : base(brand, price, iD)
        {
        }
    }

Acer.cs

    internal class Acer : Computer
    {
        public Acer(string brand, double price, string iD) : base(brand, price, iD)
        {
        }
    }

而后写仓库类用来存储电脑的品牌与数量

Cangku.cs

    internal class Cangku
    {
        //定义一个集合来存储所有的电脑
        //List<Computer> list = new List<Computer>();//如果说这样存储所有电脑的话那么分不清楚电脑品牌,所有电脑都混在一起了
        List<List<Computer>> list = new List<List<Computer>>();//这样存储将仓库根据不同电脑品牌分出不同的货架
        //比如list[0]是联想货架,list[1]是戴尔货架,list[2]是宏碁货架,list[3]是惠普货架
        public Cangku()
        {
            //在构造函数里,给仓库添加货架。
            //为什么要在构造函数里添加?因为我们要确保,仓库类一创建的时候,库里面就有东西
            list.Add(new List<Computer>());
            list.Add(new List<Computer>());
            list.Add(new List<Computer>());
            list.Add(new List<Computer>());
        }
        /// <summary>
        /// 展示仓库里的电脑信息
        /// </summary>
        public void ShowComputer()
        {
            foreach (var item in list)
            {
                Console.WriteLine("商品名称:{0},商品单价:{1},商品数量:{2}", item[0].Brand, item[0].Price, item.Count);
            }
        }

        /// <summary>
        /// 往仓库进货
        /// </summary>
        /// <param name="brand">进货的品牌名</param>
        /// <param name="count">进货的数量</param>
        public void JinHuo(string brand, int count)
        {
            for (int i = 0; i < count; i++)
            {
                switch (brand)
                {
                    case "Lenovo":
                        list[0].Add(new Lenovo("Lenovo", 1000, Guid.NewGuid().ToString()));//往第一个货架加入1000台联想电脑,Guid.NewGuid()是随机生成id的函数
                        break;
                    case "Dell":
                        list[1].Add(new Dell("戴尔", 2000, Guid.NewGuid().ToString()));
                        break;
                    case "Acer":
                        list[2].Add(new Acer("鸿基", 3000, Guid.NewGuid().ToString()));
                        break;
                    case "HP":
                        list[3].Add(new HP("惠普", 4000, Guid.NewGuid().ToString()));
                        break;
                }
            }
        }

        /// <summary>
        /// 从仓库取货
        /// </summary>
        /// <param name="brand">取货的品牌</param>
        /// <param name="count">取货的数量</param>
        /// <returns>返回类型是computer类型的数组</returns>
        public Computer[] QuHuo(string brand, int count)
        {
            Computer[] cpus = new Computer[count];
            for (int i = 0; i < cpus.Length; i++)
            {
                switch (brand)
                {
                    case "Lenovo":
                        cpus[i] = list[0][0];//取的是第一个货架上的第一个电脑,将取得的电脑给cpus的第一个元素。取完第一个之后,剩下的本来处于第二个位置的电脑就变成第一个了,所以一直取索引为“0”的
                        list[0].RemoveAt(0);
                        break;
                    case "Dell":
                        cpus[i] = list[1][0];
                        list[1].RemoveAt(0);
                        break;
                    case "Acer":
                        cpus[i] = list[2][0];
                        list[2].RemoveAt(0);
                        break;
                    case "HP":
                        cpus[i] = list[3][0];
                        list[3].RemoveAt(0);
                        break;
                }
            }
            return cpus;
        }
    }

然后写打折的类,之前我们写过,一个打折的父类和三个打折的子类

DiscountFather.cs:

internal abstract class DiscountFather
    {
        public abstract double GetMoney(double money);
    }


MNDiscount.cs:

    internal class MNDiscount : DiscountFather
    {
        public double M { get; set; }
        public double N { get; set; }

        public override double GetMoney(double money)
        {
            if (money > this.M)
            {
                return money - (int)(money / this.M) * this.N;
            }
            else
            {
                return money;
            }
        }

    }

NDiscount.cs:

    internal class NDiscount:DiscountFather
    {
        public double N { get; set; }

        public override double GetMoney(double money)
        {
            return this.N * 0.1 * money;
        }
    }

YuanJia.cs:

    internal class YuanJia : DiscountFather
    {
        public override double GetMoney(double money)
        {
            return money;
        }
    }

然后开始写SuperMarket类用于跟用户交互

    internal class SuperMarket
    {
        //初始化仓库
        Cangku ck = new Cangku();
        public SuperMarket()
        {
            //给每个货架添加数据
            ck.JinHuo("Lenovo", 100);
            ck.JinHuo("Dell", 100);
            ck.JinHuo("Acer", 100);
            ck.JinHuo("HP", 100);
        }
        public void AskBuying()
        {
            //先展示仓库库存
            ck.ShowComputer();
            Console.WriteLine("欢迎光临笔记本超市");
            Console.WriteLine("请问你需要什么?");
            Console.WriteLine("有Lenovo,Dell,Acer,HP");
            string brand = Console.ReadLine();
            Console.WriteLine("你要多少?");
            int count = Convert.ToInt32(Console.ReadLine());
            //取货
            Computer[] cpus = ck.QuHuo(brand, count);
            //算钱(原价)
            double money = CpusMoney(cpus);
            Console.WriteLine("选择打折方式");
            Console.WriteLine("1---原价  2--打9折  3--打8折  4--买500送100  5--买300送50");
            string input = Console.ReadLine();
            DiscountFather discount = GetDiscount(input);
            double realMoney = discount.GetMoney(money);//打折后的价钱
            Console.WriteLine("您本次应付{0}元,实付{1}元", money, realMoney);
            //打印小票
            ShowXiaoPiao(cpus);
        }
        /// <summary>
        /// 根据电脑的品牌返回相应的打折方式
        /// </summary>
        /// <param name="input">选择的代表电脑品牌的数字</param>
        /// <returns>相应品牌的打折方式</returns>
        static DiscountFather GetDiscount(string input)
        {
            DiscountFather father = null;
            switch (input)
            {
                case "1":
                    father = new YuanJia();
                    break;
                case "2":
                    father = new NDiscount() { N = 9 };
                    break;
                case "3":
                    father = new NDiscount() { N = 8 };
                    break;
                case "4":
                    father = new MNDiscount() { M = 500, N = 100 };
                    break;
                case "5":
                    father = new MNDiscount() { M = 300, N = 50 };
                    break;
            }
            return father;
        }

        /// <summary>
        /// 算钱
        /// </summary>
        /// <param name="cpus"></param>
        /// <returns></returns>
        public double CpusMoney(Computer[] cpus)
        {
            double money = 0;
            for (int i = 0; i < cpus.Length; i++)
            {
                money += cpus[i].Price;
            }
            return money;
        }

        /// <summary>
        /// 展示小票信息
        /// </summary>
        /// <param name="cpus"></param>
        public void ShowXiaoPiao(Computer[] cpus)
        {
            Console.WriteLine("您本次一共购买了{0}台{1}电脑", cpus.Length, cpus[0].Brand);
            Console.WriteLine("以下是小票信息");
            Console.WriteLine("========================================================");
            for (int i = 0; i < cpus.Length; i++)
            {
                Console.WriteLine("商品名称:{0},商品ID{1}", cpus[i].Brand, cpus[i].ID);
            }
        }
    }

最后在Program类里调用SuperMarket里的跟用户交互的方法即可

    internal class Program
    {
        static void Main(string[] args)
        {
            SuperMarket super = new SuperMarket();
            super.AskBuying();
            Console.ReadKey();
        }
    }

接口

定义:接口侧重表现的是一种能力,谁侧重这种能力,谁就去实现这个接口

    internal class Program
    {
        static void Main(string[] args)
        {
            IFly fly = new MaQue();
            fly.Fly();
            Console.ReadKey();
        }
    }

    class Bird //鸟
    {

    }
    class MaQue : Bird, IFly  //麻雀
    {
        public void Fly()
        {
            Console.WriteLine("麻雀会飞");
        }
    }
    class TuoNiao : Bird //鸵鸟
    {

    }
    class FeiJi : IFly    //飞机,飞机没办法继承鸟类
    {
        public void Fly()
        {
            Console.WriteLine("飞机会飞");
        }
    }
    interface IFly
    {
        void Fly();
    }

1、接口是一种规范,强调一个能力
2、只要一个类继承了一个接口,就必须实现接口中的所有成员
3、接口不允许被实例化
4、接口中的成员不允许加访问修饰符,默认为public
5、接口中的方法可以有方法、属性、索引器、事件,不能有字段和构造函数
6、接口与接口之间,可以多继承
7、接口不能继承一个类,而类可以实现一个或多个接口
8、实现接口的子类,必须实现接口的全部成员
9、一个子类可以继承一个父类和多个接口,如果一个子类继承了一个父类又继承了接口,语法上要求必须先继承父类,再继承接口
10、显示实现接口,解决方法重名问题
11、接口当一个抽象类实现的时候,需要抽象类的子类取实现接口
 

  • 25
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值