美文网首页
01.设计原则 & 设计模式

01.设计原则 & 设计模式

作者: 柏666 | 来源:发表于2019-08-03 18:26 被阅读0次

    一、七大设计原则:

        1、单一职责原则(Single Responsibility Principle):一个类只负责一项职责。一个类(或者大到模块,小到方法)职责越多,它被复用的可能性越小。类的职责有:数据职责和行为职责。数据职责通过属性体现,行为职责通过方法体现。具体处理方法就是高内聚、低耦合。把不同的职责分离成不同的类。类不要过于庞大。

        2、开闭原则(Open close Principle):开放对外扩展,关闭对内修改。设计模块时,使得该模块可以在不被修改的前提下进行扩展。一般使用抽象化的设计原则。扩展时,仅仅是对抽象类的一个实现与扩展。抽象化是开闭原则的关键,或者说是可变性封装原则,即把经常改动的模块抽象化,并封装成抽象类,如果拓展功能的话,仅仅继承抽象类并实现即可。

        3、里氏替换原则(Liskov Substitution Principle):所有能使用基类的地方,必须可以透明地使用派生类。派生类的功能是对基类的一个拓展,派生类包含了基类的所有特征,并在者基础上进行拓展。即软件中如果能够使用基类对象,那么一定能够使用其子类对象。它是实现开闭原则的一个重要手段。程序中尽量使用基类或者抽象类实现对对象的定义,运行时根据需要使用派生类对基类进行替换。

        4、依赖倒置原则(Dependence Inversion Principle):高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象定义。针对接口或抽象类编程而不是实现或具体类,即代码要依赖于抽象的类而不是具体的类。该原则也是实现开闭原则的一个重要手段。子类型能够替换掉它们的父类型。

        5、接口隔离原则(Interface Segregation Principle):大接口应该分解成小接口,客户端不必依赖于它不需要的接口。类似于单一职责原则。不要让接口臃肿。一个接口只代表一个角色,接口仅仅提供客户端需要的行为。类之间的相互依赖应建立在最小的接口上。

        6、合用复用原则(Composite/Aggregate Reuse Principle):新对象中,尽量使用组合或聚合方式实现功能,而非继承。继承会使得类之间的耦合度过高!必须使用继承时,要严格的遵循里氏代换原则。即尽量使用组合和聚合少用继承的关系来达到复用的原则。

        7、迪米特法则(Principle Of Least Knowledge):最少知识原则。一个对象应尽可能少的与其他对象发生相互作用,对象间保持最少的了解。即:如果两个类间无需直接通信,则这两个类就不应当发生直接的相互作用。如果一个类需要调用另一个类的某一方法,需要通过第三者转发这个调用。相互通信的对象必须是:

    ①、当前对象本身(this); 

    ②、以参数形式传入到当前对象方法中的对象; 

    ③、当前对象的成员对象; 

    ④、如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友; 

    ⑤、当前对象所创建的对象。 


    二、24种设计模式:

        1、设计模式是一套被反复使用,多数人知晓的经过分类的、代码设计经验的总结。它提高了代码可重用性,让代码更容易被他人理解,保证代码可靠性。

        2、种类与分类:

    种类

            创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

            结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

            行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

            3、设计模式简述:

            1、策略模式:定义了算法家族并分别封装,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

            2、装饰模式:动态给一个对象添加有一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

            3、代理模式:为其他对象提供一种代理以控制对这个对象的访问。

            4、工厂模式:定义一个拥有创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。

            5、抽象工厂模式:提供了一个创建一些列相关或相互依赖对象的接口,而无需指定它们具体的类。

            6、模板模式:定义一个操作中算法的骨架,延迟一些步骤到子类中。子类不改变算法的结构,即可重定义该算法的某些特定步骤。

            7、外观模式:为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

            8、建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

            9、观察者模式:定义一对多的依赖关系,让多个观察者对象同时监听某一主题对象。主题对象状态变化时,会通知所有观察者对象,使它们能够自动更新自己。

            10、原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

            11、状态模式:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

            12、适配器模式:将一个类的接口转换成客户希望的另外一个接口。使原本由于接口不兼容而不能一起工作的类可以一起工作。

            13、备忘录模式:不破坏封装性的同时,捕获一个对象的内部状态,并在该对象之外保存此状态。方便之后对象恢复到那个状态。

            14、组合模式:将对象组合成树形结构以表示“部分--整体”的层次结构。令用户对单个对象和组合对象的使用具有一致性。

            15、迭代器模式:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。

            16、单例模式:一个类仅有一个实例,使它们都可以独立地变化。

            17、桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。

            18、命令模式:将一个请求封装为一个对象,用不同的请求对客户进行参数化。对请求排队,或记录请求日志,以及支持可撤销的操作。

            19、职责链模式:使多个对象都有机会处理请求。避免发送者和接受者之间耦合,将这个对象连成一条链传递该请求,直到有对象处理它。

            20、中介者模式:用中介对象封装一系列的对象交互。对象间无需显式地相互引用。耦合松散,可以独立地改变它们间的交互。

            21、享元模式:使用共享技术有效地支持大量细粒度的对象。

            22、解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示中解释语言汇中的句子。

            23、访问者模式:表示一个作用于某对象结构中的各元素的操作。不改变各元素的类的前提下,可以定义作用于这些元素的新操作。


    三、常见设计模式:

        1、单例模式:一个程序中有且只能有一个对象的设计模式。它有 懒汉模式、饿汉模式 两种模式。

        ①、饿汉模式与实现:在类被加载时,自动创建单例的实例对象。不管用户是否会去调用该成员。

                Ⅰ、私有化类的构造方法。private

                Ⅱ、提供静态成员并初始化来保存唯一的实例。static

                Ⅲ、提供静态获取成员的方法来获取实例。static

        ②、懒汉模式与实现:首次调用类的方法时,会创建单例实例对象。后面再重复调用时,直接获取对象。

                Ⅰ、私有化类的构造方法。private

        ③、两者区别:

            饿汉模式:加载类时慢,运行时获取对象速度快。且是线程安全的。

            懒汉模式:加载类时快,运行时获取对象速度慢。但非线程安全的,有可能创建多个实例对象。所以需要使用线程锁。

        2、工厂模式:实例化对象,用工厂代理new操作。

        ①、几种工厂模式:

            Ⅰ、简单工厂模式:一个模块仅需一个工厂类,没有必要把它产生出来,使用静态的方法。

            Ⅱ、 多个工厂类:每个具体的产品类都对应一个创建者,每个创建者独立负责创建对应的产品对象,非常符合单一职责原则。

            Ⅲ、代替单例模式:单例模式的核心要求就是在内存中只有一个对象,通过工厂方法模式也可以只在内存中生产一个对象。

            Ⅳ、延迟初始化:ProductFactory 负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次被重用的对象保留。

        ②、抽象工厂模式:抽象工厂 => 实体工厂 => 抽象产品 => 实体产品。为创建一组相关或相互依赖的对象提供接口,无须指定具体类。

            Ⅰ、定义一个接口创建对象,让子类决定哪些类需要实例化。

            Ⅱ、工厂方法把实例化的工作推迟到了子类中去实现。

            Ⅲ、调用者直接调用工厂,由工厂创建对象,调用者不需要知道工厂内部的实现机制。

            Ⅳ、延迟初始化。

        3、代理模式:为其他对象提供代理,以控制对这个对象的访问。代理对象起中介作用,可去掉或增加一些服务。它可分为:

            ●、抽象主题类:可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。

            ●、具体类:是被委托、被代理的角色。是业务逻辑的具体执行者。

            ●、代理类:即做委托类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并在真实主题角色处理完毕前后做预处理和善后。

        ①、应用场景:

            Ⅰ、远程代理: 为不同地址空间的服务器提供局域网代理。(FQ,监控多个店铺的摄像头应用)

            Ⅱ、虚拟代理: 根据需要将一个资源消耗很大的对象进行延迟,真正需要时再进行创建。(网页中的图片或者视频异步加载)

            Ⅲ、保护代理: 增加一些权限,提供对原有对象的保护。(权限控制)

            Ⅳ、智能代理:提供目标对象额外的一些服务。(火车票代售点,中介)

        ②、代理实现的方式:

            Ⅰ、静态代理:已知代理类的情况下,代理和被代理的对象在代理前是确定的,都是实现相同接口或者继承相同的类。调用者直接调用真实角色,不用关心代理是否存在,其代理的产生由真实角色决定。即 要从真实角色查找到代理角色,不允许直接访问真实角色。 代理的管理由真实角色自己完成。使用方式如下:

                1、继承方式:

                        ①、添加一个功能接口,提供一个功能方法。

                        ②、使用目标类实现功能接口,实现功能方法。

                        ③、使用代理类继承目标类并重写功能方法,使用  super.父类功能方法。

                2、包含/合成/聚合方式:

                        ①、添加一个功能接口,提供一个功能方法。

                        ②、使用目标类,实现功能接口,并实现功能方法。

                        ③、添加一个代理类,增加一个带参的构造方法,增加一个方法,使用目标类对象调用功能方法。

                3、两种实现方式的对比:

                        ①、继承方式:代理类和被代理类耦合度高,不便于扩展。每次增加新的代理都必须重新添加新的代理类。成本较高。

                        ②、聚合方式:代理类和被代理类耦合度低,便于替换被代理类的对象,更易于复用。

                        ③、面向对象的基本原则中,推荐多使用聚合而不是继承。聚合方式更适合代理模式。

            Ⅱ、动态代理:(JDK 动态代理)根据被代理的接口生成所有的方法,被代理的类必须要实现一个接口。两条线路独立发展。动态代理实现代理的职责,业务逻辑Subject实现相关的逻辑功能,两者之间没有必然的相互耦合的关系。通知Advice从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。它是横切面编程,在不改变我们已有代码结构的情况下增强或控制对象的行为。该模式下,调用者只知道代理而不用知道真实的角色。真实角色的修改对高层模块没有影响,只需要实现接口对应的方法。适用于对扩展性要求较高的场合。 使用方式如下:

                1、添加一个功能接口,提供功能方法。

                2、使用目标类,实现目标功能接口,并实现功能方法。

                3、添加动态代理类,实现 InvocationHandler 接口,添加带有功能接口对象的构造方法,重写 InnovationHandler 中 invoke 方法:

                        Object obj = method.invoke(this.功能对象,args);

                4、获取功能接口对象,获取动态代理类对象:

                        ①、功能对象 = Proxy.newProxyInstance(代理类对象.getClass().getClassLoader(),被代理类对象.getClass().getInterfaces(),动态代理类对象);

                        ②、功能对象 = Proxy.newProxyInstance(代理类对象.getClass().getClassLoader(),new Class[]{被代理类.class} ,动态代理类对象);

                5、使用动态代理,须实现 InvocationHandler 接口,InvocationHandler 接口位于 java.lang.reflect 包下。

                        InvocationHandler 接口中只有一个方法:Object invoke(Object proxy,Method  method,Object[] args)。

                        proxy : 被代理类的对象。method : 被代理类的方法。args : 被代理的方法的参数。

                6、调用动态代理,需使用 Proxy.newProxyInstance()静态方法:

                        Proxy.newProxyInstance(loader,interfaces,handler)

                        loader : 代理类类加载器。interfaces : 被代理类实现的接口。handler : 动态代理类的实现类

                7、动态代理的好处:减少编程的工作量,加入要实现多个代理的逻辑,只要写多个代理处理器就可以了,无需每种方式都写一个代理类。系统扩展性和维护性增强。

        4、适配器模式:将一个类的接口转换为客户希望的另外一个接口。即让原本接口不匹配,不能一起工作的类可以一起工作。

            ①、组合方式(对象适配器):

                Ⅰ、添加一个元接口,增加功能方法    添加实现类,并重写功能方法。

                Ⅱ、添加一个目标接口,增加功能方法    添加实现类,并重写其功能方法。

                Ⅲ、添加适配器类 实现元接口,添加带有目标接口对象的构造方法,重写元方法,使用目标接口对象调用目标方法。

            ②、继承方式(类适配器):

                Ⅰ、添加一个元接口,增加功能方法    添加实现类,并重写功能方法。

                Ⅱ、添加一个目标接口,增加功能方法    添加实现类,并重写其功能方法。

                Ⅲ、添加适配器类 继承目标接口的实现类  并实现元接口,重写元接口中的方法,采用super.目标方法()调用。

            ③、区别: 

                Ⅰ、组合方式(对象适配器):把"被适配器"组一个对象组合到适配器中,以修改目标接口包装被适配器。

                Ⅱ、继承方式(类适配器):通过多重继承不兼容的接口,实现对目标接口的匹配,单一为某个类的实现而适配。

                Ⅲ、特点: 透明、重用、低耦合。用一个已经存在类的接口时,如果接口或方法与你的要求不同时,可以采用适配器模式。

        5、策略模式:定义算法家族并分别封装,让它们之间可以互相替换。算法变化不会影响调用算法的用户。策略算法是相同行为的不同实现。

            ①、实现步骤:

                Ⅰ 、添加抽象的策略父类 Strategy。

                Ⅱ 、添加实现抽象类的子类。

                Ⅲ 、定义 Factory 工厂生产策略对象。

                Ⅳ、定义 Content 调用功能。

            ②、优点:

                Ⅰ 、提供了管理相关的算法家族的办法。

                Ⅱ 、提供了可以替换继承关系的办法。

                Ⅲ 、避免使用多重条件转移的语句。

            ③、应用场景:多个类只区别在表现行为不同,可以使用 Strategy 模式,在运行时动态选择具体要执行的行为。

        6、模板模式:定义一个操作中算法的骨架,步骤在子类中实现。子类不改变算法结构,即可重定义该算法的某些特定步骤。为防止恶意操作,一般模板方法都加上final关键字,不允许被覆写。

            ①、基本方法:

                Ⅰ 、在模板方法中添加可被子类重写,方法返回值为 Boolean,来判断是否需要执行特定的操作。子类实现方法并在模板调用。

                Ⅱ 、具体步骤:

                    1、多个子类有公有的方法,并且逻辑基本相同时。

                    2、重要、复杂的算法,把核心算法设计为模板方法,其余功能则由各个子类实现。

                    3、重构时,把相同的代码抽取到父类中,然后通过钩子函数(见Ⅰ)约束其行为。

            ②、优缺点:

                Ⅰ 、在一个类中形式化地定义算法,而由它的子类实现细节的处理。

                Ⅱ 、是一种代码复用的基本技术。

                Ⅲ 、反向的控制结构。“好莱坞法则”,即父类调用子类,通过对子类的扩展增加新的行为,符合“开闭原则”。

                Ⅳ、但会导致类的个数增加,但更符合“单一职责原则”。

            ③、适用场景:一次实现一个算法的不变部分,并将可变的行为留给子类来实现,需要通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。

        7、建造者模式:将复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

            ①、组成部分:

                Ⅰ、产品类:通常实现了模板模式,即有模板方法和基本方法。

                Ⅱ、抽象建造者:父类方法等。一般由子类实现并组建规范的产品。

                Ⅲ、具体建造者:实现抽象类定义的所有方法,并返回组建好的对象。

                Ⅳ、导演类:安排已有模块的顺序,然后告诉Builder开始建造。

            ②、使用场景:

                Ⅰ、相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式。

                Ⅱ、多个部件或零件,都可以装配到一个对象中。但产生的运行结果又不相同时,可以使用该模式。

                Ⅲ、产品类非常复杂,或类中的调用顺序不同产生了不同的效能,适合用建造者模式。

            ③、建造者模式与工厂模式的不同:

                Ⅰ、建造者模式:基本方法的调用顺序安排,这些基本方法已经实现了,顺序不同产生的对象也不同;

                Ⅱ、工厂方法:主要用于创建。组装顺序则不是它关心的。

            8、原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。实际上就是实现Cloneable接口,重写clone()方法。

            ①、特点:

                Ⅰ、性能优良。原型模式是内存二进制流的拷贝,比new对象性能好很多,尤其在循环体内产生大量对象的情况下。

                Ⅱ、逃避构造函数的约束:既是优点也是缺点,直接在内存中拷贝,不会执行构造函数。

            ②、使用场景:

                Ⅰ、资源优化:类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。

                Ⅱ、性能和安全要求:new产生对象需要非常繁琐的数据准备或访问权限。

                Ⅲ、一个对象多个修改者:一个对象要供其他对象访问,调用者们可能都需要修改其值。原型模式拷贝多个对象供调用者使用。

            ③、浅拷贝和深拷贝:

                Ⅰ、浅拷贝:Object类提供的方法clone只拷贝本对象,不拷贝对象内部的数组、引用对象等。它们还是指向原生对象的内部元素地址。其他原始类型(int、long、char、string)会被拷贝。

                Ⅱ、深拷贝:对私有的类变量进行独立的拷贝。如:thing.arrayList = (ArrayList<String>)this.arrayList.clone();

            ④、原型模式下,引用的成员变量两种情况下不会被拷贝:

                Ⅰ、类的成员变量,而非方法内变量。

                Ⅱ、必须是一个可变的引用对象,而非一个原始类型或不可变对象。

            9、中介者模式:用中介对象封装一系列的对象交互,适用于多个对象紧密耦合的情况。紧密耦合的标准是:在类图中出现了蜘蛛网状结构,即每个类都与其他的类有直接的联系。

                ①、组成部分:

                    Ⅰ、抽象中介者:定义统一的接口,用于各同事角色之间的通信。

                    Ⅱ、具体中介者:通过协调各同事角色实现协作行为。它必须依赖于各个同事角色。

                    Ⅲ、同事:同事间通信时,需通过中介者角色协作。同事类行为分为两种:

                        1、同事本身的行为(自发行为),与其他的同事类或中介者没有任何依赖。比如改变对象本身的状态,处理自己的行为等。

                        2、必须依赖中介者才能完成的行为,叫做依赖方法。

                ②、ps:

                    Ⅰ、使用同事类注入而非抽象注入的原因:抽象类中不具有每个同事类必须要完成的方法。即每个同事类中的方法各不相同。

                    Ⅱ、同事类要使用构造函数注入中介者,因为同事类必须有中介者。中介者使用getter/setter方式注入同事类,因为中介者却可以只有部分同事类。

    https://www.cnblogs.com/yunfeioliver/p/9306355.html

    相关文章

      网友评论

          本文标题:01.设计原则 & 设计模式

          本文链接:https://www.haomeiwen.com/subject/tnmldctx.html