• 设计模式(一)-设计原则(1)


    六大设计原则

    1、单一职责原则

    特点: 类和方法属性等,都应当遵守单一职责。尽可能保持统一性,单一性。
    含义:
    (1)统一性,定义一个模块就必须要符合所有对象的行为特征。比如声明一个 Animal 类,有飞、呼吸等行为。而如果实例化一个动物鱼,则鱼不符合飞的行为特征,也就不满足统一性。
    (2)单一性。只做一种事情。比如声明一个文件下载上传的类。在类里面,不仅要实现文件的下载上传,还对文件进行读写的功能等。该类在这过程中做了多件不同的事情,不符合单一职责原则。

    优点: 高内聚,修改当前模块对其他模块的影响很低。
    缺点: 过度单一,过度细分,就会增加系统的复杂程度。

    反例:

        public class Files
        { 
            // 违反单一性:该类处理三种不同的逻辑,分别为接受发送文件、读写文件、连接网络。
            //1.负责接受发送文件
            public void ReveFile() { }
            public void SendFile() { }
            //2.负责读写文件
            public void Write(){}
            public void Read(){}
            // 不具有统一性,如果是 PDF 文件,则不能通过 TXT 记事本来打开。
            public void OpenToTxt(string file) { }
    
            //3:处理网络的连接
            public void SocketFor(string socketType) {
                //在此方法内,实现了多种不同的逻辑,同样不符合单一职责原则
                /*
                
                //1).sokcet 绑定
                Socket.Binding();...
                //2). 检测网络问题
                if (CheckNet.IsNet) setNet else Failed Link.. 
                //3).更新页面上的网络状态
                Task.Run(=> NetState.Refresh...)...
                
                */
            }
    
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    正例:

        //单一职责:通过 soket 来接受发送文件
        public class HttpsFile
        {
            Sockets socket;
            public void Reve() { }
            public void Send() { }
        }
    
        //单一职责:读写文件
        public class CustomFile
        {
            public void Write() { }
            public void Read() { }
            public void OpenToTxt(string file) { }
        }
    
        //单一职责:连接网络
        public class Sockets
        {
            public Sockets(string type) { }
            private void Bingding() { }
            private bool CheckNet() { return true; }
            private void RefreshState() { }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    2、开闭原则

    特点: 把类、模块、函数等抽象化,对外扩展,对内不得修改。
    含义:用伪代码理解-》

    public interface IFile
    {
    //...不得修改接口内部的代码
    }
    
    //所谓对外,就是对实现不同的类进行扩展。
    //1.扩展 Word 类,Excel 类...
    public class Word:IFile,IReader...
    {
    //2.扩展新功能 void setOutLine();...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    优点: 新加功能不需要对已有的模块进行修改,而是对外扩展来实现。

    反例:

        public class OpenFile
        {
            public void OpenToTXT(string fileName)
            {
                Console.Write("Open txt 文件");
            }
    
            public void OpenToPDFReader(string fileName)
            {
                Console.Write("Open pdf 文件");
            }
    
            //会潜在新需求
            //比如通过浏览器打开html文件、通过视频软件打开mp4等等
            //......
    
            //该类违反了开闭原则。一旦有新需求就会对该类进行修改代码。
            //这会影响其他引用该类的代码逻辑
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    正例:

        //把打开文件模块抽象化
        public interface IOpenFile
        {
            
           void Open(IFileType fileType);
        }
    
        //把文件类型模块抽象化
        public interface IFileType
        { 
            String format { get; }
        }
    
        //修改特征
        public class PDFFormat : IFileType
        {
            public String format { get { return "PDF"; } }
        }
    
        //修改特征
        public class SQLFormat : IFileType
        {
            public String format { get { return "mdf"; } }
        }
    
        //修改特征
        public class OpenPDF : IOpenFile
        {
            public void Open(IFileType fileType)
            {
                Console.WriteLine("Opened File is " + fileType.format);
            }
        }
    
        //扩展新功能
        public class OpenSQL : IOpenFile
        {
            public void Open(IFileType fileType)
            {
                var bstate = linkSqlManager("123");
                if (bstate)
                    Console.WriteLine("Opened File is " + fileType.format);
            }
    
            //新功能:因为查看SQL文件,需要先连接数据库
            private bool linkSqlManager(string pwd)
            {
                string stateStr = pwd == "123" ? "连接成功" : "连接失败";
                bool result = pwd == "123" ? true : false;
    
                Console.WriteLine(stateStr);
                return result;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                var openPDF = new OpenPDF();
                openPDF.Open(new PDFFormat());
                var openSQL = new OpenSQL();
                openSQL.Open(new SQLFormat());
            }
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    3、依赖倒置原则

    特点: 依赖于抽象接口,不依赖于具体实现。
    依赖性: A类依赖B类,当B类修改或消失时,对A类会影响很大。

    符合依赖倒置原则:

    • A类作为主调用者,不应该依赖被调用者B类。A类和B类应当都依赖于抽象。即A类依赖于IA接口,B类依赖于IA接口。

    **优点:**降低了模块之间的耦合性。

    反例:
    假如有制造交通工具的工厂,根据车的型号来生产一辆车。

    
        public class Factory
        {
        //Factory 类依赖 Vehicle 类,
        //当 Vehicle 内部被修改时或者消失掉,将会影响到 Factory 类的 make 方法
            public void make(Vehicle v) {
                Console.WriteLine(v.model + v.color);
            }
        }
    
    //如果工厂需要同时制造一辆自行车和汽车时,则生产的需求就不同了。
    //在这样的情况下,会修改Vehicle 类,会可能影响这两种车的制造。
    //比如汽车需要设计安全带,自行车需要车篮。
    
        public class Vehicle
        {
            public string model { get; private set; }
            public string color { get; private set; }
    
            public Vehicle(string model, string color)
            {
                this.model = model; this.color = color;
            }
        }
        
    class Program
    {
    static void Main(string args)
    {
         Vehicle v = new Vehicle("奥迪","黑色");
         Factory factory = new Factory();
         factory.make(v);
    }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    正例:

    (1)定义设计交通工具的接口:

    
        //设计交通工具抽象化,作为父接口
        public interface IVehicle
        {
            string Model { get; }//车类型
            string AppearColor { get; }//外观颜色
            string Design();//设计工作
        }
    
        //设计自行车抽象化,继承 IVehicle
        public interface IBicycle : IVehicle
        {
            string Basket { get; }//车篮
        }
    
        //设计汽车抽象化,继承 IVehicle
        public interface ICar : IVehicle
        {
            string Safetybelt { get;}//安全带
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    (2)实现交通工具的接口:

        public class Car: ICar
        {
            private string _model;
            public string Model { get { return _model; } }
            private string _safetybelt;
            public string Safetybelt { get { return _safetybelt; } }
    
            public Car(sstring model, string safetybelt)
            {
                _model = model;
                _safetybelt = safetybelt;
            }
    
            public string Design()
            {
                return Model + Safetybelt;
            }
    
        }
    
        public class Bicycle : IBicycle
        {
            private string _model;
            public string Model { get { return _model; } }
            private string _basket;
            public string Basket { get { return _basket; } }
    
            public Bicycle(string model, string basket)
            {
                _model = model;
                _basket = basket;
            }
    
            public string Design()
            {
                return  Model + Basket;
            }
    
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    (3)定义工厂的接口:

       public interface IFactory
        {
            string who();
        }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (4)实现工厂的接口:

        public class CarFactory : IFactory
        {
            public string who()
            {
                return "汽车制造商";
            }
        }
    
        public class BicycleFactory : IFactory
        {
            public string who()
            {
                return "自行车制造商";
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    (5)中间类用来处理 IVehicle 接口(父接口) 和 IFactory 接口:

    
        public class Maker
        {
        //由于传入参数为原始(父接口)的接口类型,不管外面传入的实现类是什么,将来会修改成什么样子
        //只要实现类依赖于这些接口,就不会影响到该类执行的代码逻辑。
            public void Work(IFactory factory, IVehicle vehicle)
            {
                Console.WriteLine(factory.who() + vehicle.Design());
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Maker maker = new Maker();
    
                CarFactory carFactory = new CarFactory();
                Car car = new Car("奥迪", "三点式安全带");
                maker.Work(carFactory, car);
    
                BicycleFactory bicycleFactory = new BicycleFactory();
                Bicycle bicycle = new Bicycle("欧拜克","灰色车篮");
                maker.Work(bicycleFactory, bicycle);
                
                //工厂类和交通工具类在各自内部之间不存在互相依赖,不存在调用和被调用的关系。
                //这样就降低了模块之间的耦合性。
                //maker 作为中间类,Work 方法也只是依赖了接口,而并非实现类。
            }
    
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    4、里氏替换原则

    特点: 定义一个父类,其子类就必须完全继承父类的所有特征。这样父类替换成子类后,也不会有任何变化。

    反例:

        public abstract class Vehicle
        {
            public abstract void Driver();
        }
    
        public class Car : Vehicle
        {
            public override void Driver(){}
        }
    
    //把 父类 Vehicle 可以替换成 Car 类来使用,完全不会影响。因为 Car 继承了 Driver 方法。
    //但是如果自行车 Bicycle 继承于 Vehicle,问题就来了。
        public class Bicycle: Vehicle
        {
        //继承 Driver 方法显然是不合理的,自行车是骑的,而不是开的
            public override void Driver() { }
        }
        
    // 也就是说,Bicycle 类没有完全可以实现 Vehicle 里的所有方法属性等。
    //所以违反了里氏替换原则
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    正例:

    
      public abstract class Vehicle
        {
            public abstract void Move();
        }
    
    public class Car: Vehicle
        {
            public override void Move() { Console.Write("Driver");}
        }
        
    public class Bicycle: Vehicle
        {
            public override void Move() { Console.Write("By Bike");}
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    5、接口隔离原则

    特点:定义一个接口时,不能定义一些不需要的方法属性等。应当要建立在最小的接口上。(接口里的所有特征应当都要被用于实现类上)

    反例:

    
        public interface IAnimal
        {
            void Eat();
            void Talk();
            void Look();
            void Fly();
            void Run();
            void Play();
            //还有其他行为......
        }
    
        public class Cat: IAnimal
        {
            public void Eat() { Console.WriteLine("吃鱼"); }
            public void Talk() { Console.WriteLine("喵!"); }
            public void Look() { Console.WriteLine("眨眼"); }
            public void Fly() { Console.WriteLine("..."); }
            public void Run() { Console.WriteLine("跑"); }
            public void Play() { Console.WriteLine("啃纸皮"); }
            //还有其他行为......
    
            //问题1:猫不会飞,自然不要有这样的一种行为出现。即Fly 方法不应该出现。
        }
        class Program
        {
            static void Main(string[] args)
            {
            //问题2:Cat 依赖 IAnimal接口后,只看到了猫在玩耍的一种行为。
            //而其他那些行为,可以说是基本上没什么用了。
                Cat cat = new Cat();
                cat.Play();
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    反例2:

    
    //如果把 Animal 接口进一步拆分成:
        public interface IMouth
        {
            void Eat();
            void Talk();
        }
    
        public interface IEar
        {
            void Look();
        }
    
        public interface Iwing
        {
            void Fly();
            void Swim();
        }
    
        public interface IFoot
        {
            void Run();
            void walk();
            void Climb();
        }
        
    //同样也是违反了接口隔离原则,因为比如:
    //Iwing 有 飞和游泳的特征。鱼儿可以用翅膀游泳,小鸟可以扇动翅膀飞翔。
    //但是鱼儿不能飞,一些小鸟不能游泳。
    
    //显然不满足这个条件:
    //接口里的所有特征应当都要被用于实现类上
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    正例:

    //拆分成最小的接口
    public interface IEat
        {
            void Eat();
        }
    
        public interface IPlay
        {
            void Play();
        }
    
        public class Cat:IEat, IPlay
        {
            public void Eat() { Console.WriteLine("猫在吃鱼干"); }
            public void Play() { Console.WriteLine("猫在啃纸皮"); }
        }
    
    //解决1:Eat 和 Play 是猫的行为特征,都是需要的。
    //解决2:只定义接口 IEat 和 IPlay,是最小的接口,节省了很多不必要的行为。
    //如果有新的需求,猫要喵喵叫,就直接再定义一个“叫”行为的接口。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    6、迪米特法则(最少知识原则)

    原则1: 减少模块与模块之间的依赖性。比如模块 A 和模块 B 互相依赖,模块C和B互相依赖,而 C 跟 A 没有关系。C 里没有 A的存在,反之亦然。

    原则2: 降低模块与模块之间的耦合性。比如 A 被 B 调用,A 类存在于 B 类里。但是 A 的部分成员都设置为私有,不能访问 A 类 的私有成员, B 类只能访问 A类的公有成员。
    (public、private 等访问机制,要根据情况合理设定,不要滥用)

    优点: 减少模块间的依赖,降低模块间的耦合性。提高代码的复用率。

    反例:
    根据以上ABC类描述的,数据分析师类作为 C 类,数据管理员类作为 B 类,顾客类作为 A 类。

    假定以下代码没有遵循原则1 和原则2:

    • 违反原则1:C 类能够直接对A类操作,A存在于C类。
    • 违反原则2:A 类的一些pwd、isVIP等重要成员以 public 对外开放,导致外部类可以轻易修改这些成员的逻辑。
    
        //顾客
        public class Customer
        {
            public string pwd;
            public bool isVIP;
            public string name { get; private set; }
        }
        
    //数据分析师
        public class DataAnalyst
        {
            // DataAnalyst 和 Customer 存在依赖关系。
            //如果 Customer 逻辑被修改,则会影响到 DataAnalyst。
            public void GetCustomersData(DataManager manager)
            {
                var customers = manager.GetCustomers();
                var count = customers.Count;
                //如果 isVIP 字段访问改成 private,则会报错。
                var vip = customers[0].isVIP;
            }
        }
    
        //数据管理员
        public class DataManager
        {
            private List<Customer> Customers;
            public List<Customer> GetCustomers() { return Customers; }
        }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    正例:

    
    
        //顾客
        //私有变量限制 DataManager 对 Customer的操作范围
        public class Customer
        {
            private string pwd;
    
            private bool _isVIP;
            private bool isVIP { get { return _isVIP; } }
    
            public string name { get; private set; }
    
            public void setVip(bool isvip)
            {
                _isVIP = isvip;
            }
        }
    
        //数据分析师
        //只需要通过 DataManager 间接获取 Customer 的一些信息。
        //保证了 DataAnalyst 和 Customer 不存在依赖关系。
        //Customer 即便修改了代码逻辑,也影响不了 DataAnalyst。
        public class DataAnalyst
        {
            public void GetCustomersData(DataManager manager)
            {
                var count = manager.GetCustomerCount();
            }
        }
    
        //数据管理员
        public class DataManager
        {
            private List<Customer> Customers;
            private void setVip(int index) { Customers[index].setVip(true); }
    
            public int GetCustomerCount() { return Customers.Count; }
          
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
  • 相关阅读:
    文件系统相关
    如何当好硬软件助理工程师——实习周报(一)
    GE IS420UCSBH1A 自动化控制模块
    vue项目优化
    IOS工程:如何在apple后台为app添加沙盒测试账户
    视频孪生,打造虚实共生的数字孪生应用
    stm32 下载程序只能使用串口1
    R23C02版本正式发布 | 更智能、更稳定的菊风视频能力平台
    vs工程添加自定义宏
    windows平台编译OpenCV以支持CUDA
  • 原文地址:https://blog.csdn.net/chen1083376511/article/details/134453623