设计模式------设计原则

前言:

推荐几本相关的书:

(1)Head First Design Patterns

曾经买Head First系列的时候买的一本书,是java语言的案例,但是完全不影响你了解设计模式。这系列的书就是有很多图,做快速了解建议买。

(2)大话设计模式

1个月前买的,看作者简介是名老师,里面就是菜鸟和大鸟的对话举出很多例子,案例也相当不错。这本书最起码让我感觉特别不错。

(3)重构与模式

这本是必须要看的一本书,前几张讲了什么是重构,什么是模式。然后两者之间的关系。后边是是讲设计模式的动机,做法,实例,变体。也不分什么创建,行为,结构什么的。最后一章是重构的实现。

一.设计原则:

单一职责原则告诉我们实现类要职责单一;

里氏替换原则告诉我们不要破坏继承体系;

依赖倒置原则告诉我们要面向接口编程;

接口隔离原则告诉我们在设计接口的时候要精简单一;

迪米特法则告诉我们要降低耦合。而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。

1.开闭原则OCP(Open-Close Principle)

  【开指的是对扩展开放,关指的对修改关闭。】

  我把它理解为“一国两制”原则。一国两制怎么说:香港澳门继承了中国这个类,表示说:一个中国不可改变,但针对与港澳实际情况,他们实行的是资本主义经济。

下面看一个简单的例子:

  如果要开始编程开发的话显然你要学一门语言,假如你先学了语言是c#。

//为了编程我学习了c
    public class Clanguage
    {

        public void doStudent(int years)
        {
            Console.WriteLine("我学习了" + years + "年");
        }
    }
    //服务端调用
    public class CService
    {
        private Clanguage C_language = null;
        public CService()
        {
            C_language = new Clanguage();
        }
        public void doStudent(int yesrs)
        {
            C_language.doStudent(yesrs);
        }
}
//客户端调用
 CService server = new CService();
 server.doStudent(2);

  假如过一段时间我又想进行web开发,这时候是不是我就要在加一个类,接着服务类也要再写个调用。随着开发的过程我有想学些动态语言以加快开发效率是不是要依次类推下去。

这样是不是要来回增加学习语言类class netlanguage,class pythonlanguage,还要修个server。。。。。

//为了编程我学习了c
    public class Clanguage
    {

        public void doStudent(int years)
        {
            Console.WriteLine("我学习了" + years + "年");
        }
    }
 public class Netlanguage
    {

        public void doStudent(int years)
        {
            Console.WriteLine("我学习了" + years + "年");
        }
    }
........
    //服务端调用
    public class Service
    {
        string otype = "";
        private Clanguage C_language = null;
        private Netlanguage net_language = null;
        public Service(string type)
        {
            otype = type;
            if (type == "c")
                C_language = new Clanguage();
            else if (type == "net")
                net_language = new Netlanguage();
        }
        public void doStudent(int yesrs,)
        {
            if (otpye == "c")
                C_language.doStudent(yesrs);
            else if (otype == "net")
                net_language.doStudent(yesrs);
        }
    }  
.........

  这个时候你想到了什么?对的接口,这也是开闭原则的主要思想,面向抽象接口编程而非类编程

 public interface Ilanguage
    {
        void doStudent(int years);
    }
    //为了编程我学习了c
    public class Clanguage:Ilanguage
    {

        public void doStudent(int years)
        {
            Console.WriteLine("我学习了" + years + "年");
        }
    }
    public class netlanguage : Ilanguage
    {

        public void doStudent(int years)
        {
            Console.WriteLine("我学习了" + years + "年");
        }
    }
    //服务端调用
    public class Service
    {
        private Ilanguage language = null;
        public Service(Ilanguage I_language)
        {
            language = I_language;
        }

        //节日问候
        public void doStudent(int yesrs)
        {
            language.doStudent(yesrs);
        }
    }

  对比可得:遵循面向接口编程【即遵循开闭原则】的编程我们只需要扩展我们的语言类型,无需改变我们的server。开:扩展开发,闭:修改关闭。

2.单一职责原则RRP(Single Responsibility Principle)

  【一个类应该只有一个发生变化的原因,职责分离】

  高内聚低耦合这就是我们写程序的目标,但是很多时候高耦合会在不经意间就产生了,这大多是因为职责扩散造成的。这个原则最好理解,又最容易违背这个原则。原因就是职责这个家伙不好确认。

继续看一个例子:

  开发过程其实不是跑到工地上干的工人瓦工电工木工什么都会,就会获得老板同事的爱戴{ps:想到辍学那两年水电工的生活。}一个类让它什么都干会有什么问题呢?看下面的例子。

好吧就以工人为例。那时候的生活无非就是吃完饭上班,上完班回去玩手机打牌。。。。【ps:首先接口的名称我想不起来命名为什么。暂且定义为istrive】

 public interface istrive
    {
        //睡觉
        void Shuijiao();
        //玩手机
        void WanShouji();
        //打牌
        void DaPao();
        //购物
        void GouWu();
        //刷漆
        void Shuaqi();
        //布线
        void Buxian();
        //粉墙
        void Fenqiang();
        //吊顶
        void Diaoding();
    }
、、、、、、、、、

  从接口上你发现了什么,奋斗这个接口是把休息的内容和工作的内容放到了一起这个时候,显然不管是工作的内容还是休息的内容都有可能发生改变,这样显然是不合理的,两者会互相影响。

所以就可以改成两个接口

 public interface iwork
    {
        //刷漆
        void Shuaqi();
        //布线
        void Buxian();
        //粉墙
        void Fenqiang();
        //吊顶
        void Diaoding();
    }
 public interface irest
    {
      //睡觉
        void Shuijiao();
        //玩手机
        void WanShouji();
        //打牌
        void DaPao();
        //购物
        void GouWu();
}
、、、、、、、、、

//接口的实现

这样休息就是休息,工作就是工作互不影响,也不用担心我修改工作类的时候休息类受到影响。这个职责划分有时确实不好划分。

3.依赖倒转原则DIP(Dependency Inversion Principle)

  【抽象不应当依赖于细节,细节应当依赖于抽象;高层实现不依赖底层实现。】 想想让你封装一个类的时候你首先会做什么。会先封装接口,再写实现。{#总工说这样处理才是合理的。原因就在这#}。面向接口编程而非实现。这个原则在我看来也是面向对象设计的标志。

  举个例子:usb是不是所有的的电脑都能通过usb接口连接。如果联想的usb接口和苹果的usb接口不一样,那么你买了一个200多的USB键盘,结果是不是就不能公用了。

依照上面的例子我们先看一个没有问题又存在问题的例子:

我们来用tankpad下载资料传递到u盘:

 public class tankpad
    {
        public void download()
        {
            Console.WriteLine("下载资料");
        }
    }

    public class usb
    {

        public void insert(tankpad tp)
        {
            tp.download();
          
        }
    }

  完全没问题,但是现在我们如果在用usb传递mac下载的资料传递到p盘的时候是不是就没那么顺利了,。因为我们的usb定义了insert的方法只能用作传递tankpad的下载内容。但显然usb是不分电脑型号的。那么这个时候我们就要遵循dip原则。

下面是针对usb传递各中电脑下载资料的方法:

 public interface IPC
    {
        void download();
    }
    public interface IUsb
    {
        void insert(IPC pc);
    }
    public class usb : IUsb
    {
        public void insert(IPC pc)
        {
            pc.download();
        }
    }

  这个时候不管是tankpad,还是mac还是其他电脑都可以继承ipc接口,然后一切搞定。抽象不依靠细节,抽象值得就是IPC细节就是各种电脑。当然我们如果把usb也同样抽象,因为也有不同的usb,这样的话两者可以并行开发互补影响,这就是tdd(测试驱动开发,这是后话,会有专门的一个篇章叙述它。)OK下一个原则走起。

4.里氏代换原则Liskov Subsitution Principle(LSP)

  【子类可以扩展父类的功能,但不能改变父类原有的功能】

  里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

有这么一句话:里氏代换原则是继承复用的一个基础。

  检验你是否遵循了里氏代换原则的方法:如果调用的是父类的话,那么换成子类也完全可以运行。

注:我在网上看过一个“企鹅不是鸟”的例子,企鹅是鸟吗?企鹅显然是鸟没时间翻百科全书,在网上搜一下也能发现企鹅是鸟,因为恒温动物分两种鸟类和哺乳类,企鹅是卵生,不哺乳所以他是鸟类。那么我们变成的过程当中是不是就会这样写。

看代码了解这个小栗子:

  public interface IBird
    {
        //有羽毛
        void feather();
        //卵生
        void ovipara();
        //不哺乳
        void nolactation();
        //会飞
        void fly();
    }

  以上是不是我们在生活当中对鸟类的理解,这个应该没有异议吧,那么接下来我们让企鹅继承这个接口会怎么样?????

发现不能继承他为什么【企鹅不会飞】,继承这个鸟类的接口就会出现问题。这就是违背了LSP原则,为此我们应该是理解了LSP了吧。

5.接口隔离原则Interface Segregation Principle(ISP)

  从字面上来讲就是一个不要把接口写的太臃肿。查资料大致说的就是有两种分离方式一种是“定制服务”和“角色隔离”。定制服务:大致来讲就是我针对一个客户端,我的一些方法放到一个接口里,另一个客户端我的一个类放在另一个接口里面。角色隔离:是指一个客户端有多个方法,多个方法写多个接口。

单一职责与接口隔离的异同点:

  到了这里,有些人可能觉得接口隔离原则与单一职责原则很相似,其实不然。

  第一,单一职责原则原注重的是职责;而接口隔离原则注重对接口依赖的隔离。

  第二,单一职责原则主要是约束类,其次才是接口和方法,它针对的是程序中的实现和细节;而接口隔离原则主要约束接口接口,主要针对抽象,针对程序整体框架的构建。

【友情提醒:接口也不要分的太细,要不然结果就是接口太多。】

上班是什么:工作就是我努力工作然后领工资。所以就有下面的一个接口

 public interface IJob
    {
        //工作
        void Work();
        //领工资
        void Getpaid();
    }

在你的公司里面同为上班的有一种人事不领工资的,他还发工资。他就是万恶的资本家你的老板:)。

所以针对这一块就要接口隔离。

 public interface IWork()
    {
        //工作
        void Work();
      
    }
 public interface IGetpaid()
    {
  //领工资
        void Getpaid();
}
//员工
public class staff:IWork, IGetpaid
{
public   void Work()
{
}
public   void  Getpaid()
{
}
//老板
public class boss:IWork
{
public   void Work()
}
}

  好好理解一下他与单一职责的异同

6.迪米特原则Law of Demeter 又称Least Knowledge Principle(LKP)最少知识原则

【我的理解就是:这个原则不希望类与类之间建立直接联系。】简单来说就是不和陌生人说话。就像北京的租房,你找到的是中介,往外租房的房东也是找到的是中介,你与房东不交流。

降低类本身和成员的访问权限,达到【低耦合,高内聚】是其目的。

【和ISP接口隔离原则一样,限制类与类之间的通信。ISP限制的是宽度,而LoD迪米特原则限制的是通信的广度和深度。】。

外观模式(Facade Pattern)和中介者模式(Mediator Pattern)就使用了迪米特法则。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏木可大大

面向对象设计必须遵循的几条原则

我们应该采用何种方法去应对需求变化呢?首先,在方法论层面我们应该采用敏捷开发;其次,在代码层面,使用OOD(Object-Oriented Design,面向对...

784
来自专栏技术博客

设计模式之前奏(UML类图)

本人菜菜一个,最近一直在博客园游走闲逛,看到了各种技术,各种各种……。便看到了大话设计模式这本书,下了电子版的看了看第一章,感觉相当不错,不仅通俗易懂,而且与实...

563
来自专栏walterlv - 吕毅的博客

NullReferenceException,就不应该存在!

2017-11-29 16:08

371
来自专栏IT派

细思极恐-你真的会写java吗?

自2013年毕业后,今年已经是我工作的第4个年头了,总在做java相关的工作,终于有时间坐下来,写一篇关于java写法的一篇文章,来探讨一下如果你真的是一个ja...

965
来自专栏风中追风

敏捷软件开发学习笔记

敏捷设计:敏捷设计是一个过程,不是一个事件,它是一个持续的应用原则、模式以及实践来改进软件的结构和可读性的过程,它致力于保持系统设计在任何实践都尽可能得简单,干...

3349
来自专栏程序员与猫

使用抽象类和接口的优解

1. 前言 笔者相信,每个使用面向对象语言的开发者自编码以来,肯定便琢磨过抽象类(Abstract)和接口(Interface)的区别。可能一些人已经找到了适...

1945
来自专栏编程

设计模式六大原则(3):依赖倒置原则

定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。 问题由来:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过...

1607
来自专栏小蠢驴iOS专题

Masonry拓展-链式编程 & 函数式编程

1064
来自专栏Java技术

为什么要有Spring AOP?

上一篇从Web开发演进过程的一个侧面简述了一下为什么会有Spring?事实上只介绍了为什么会有Spring IOC(控制反转/依赖注入)以及Spring IOC...

381
来自专栏大数据钻研

如何设计优雅的类结构

注:正文中的引用是直接引用作者作者的话,两条横线中间的段落的是我自己的观点,其他大约都可以算是笔记了。 「Clean Code」这本书从这一章开始文风有些变化,...

2566

扫描关注云+社区