精华内容
下载资源
问答
  • 我们都去过奶茶店买过奶茶吧,一种奶茶可能有很多不同的产品,同一种产品也有很多不同的口味。我们去买的时候,都会发现我们的奶茶是现场调制的...一、认识装饰模式我们先给出装饰模式的概念,再去分析一下:装饰模...

    我们都去过奶茶店买过奶茶吧,一种奶茶可能有很多不同的产品,同一种产品也有很多不同的口味。我们去买的时候,都会发现我们的奶茶是现场调制的,奶茶店会根据已有的很多奶茶,添加不同的口味。再比如新买的房子去装修,房子是不会变的,但是我们可以装修成不同的风格。这一过程就是装饰过程。其思想就是装饰模式。这篇文章将通过案例对装饰模式有一个了解和分析。

    一、认识装饰模式

    我们先给出装饰模式的概念,再去分析一下:

    装饰模式又名包装模式。装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。有透明和半透明两种,大部分都是半透明的,半透明的装饰模式是介于装饰模式和适配器模式之间的。

    概念看起来有点懵,我们通过奶茶例子来解释一下:

    40e7abcc7bf5b43633bb11cefd51ec69.png

    从上面这张图我们可以看到,我们去店里买一杯奶茶有10种,还有6种口味可供选择,如果我们把不同的奶茶都当成一个类,那我们就需要new出来6*10个类。想想这样也太多了,使用了装饰器模式,使用18个类(10个类表示奶茶,6个类表示口味、其他两个分别是奶茶和口味的接口抽象)就能生产任何奶茶了,是不是很方便。

    现在来看看透明和半透明的区别:

    透明的装饰模式,要求具体构件角色、装饰角色的接口与抽象构件角色的接口完全一致。意思是奶茶接口、珍珠奶茶和蜂蜜奶茶、调料的这些接口一样。下面代码中会有体现。相反,如果装饰角色的接口与抽象构件角色接口不一致,那就是半透明的了。

    我们来看看装饰模式的类图:

    3f7a3c6a8d9ba8a95da8e461618fea98.png

    从上图我们可以看到,装饰模式一共有四个角色。

    (1)Component(抽象构建):这里指的奶茶抽象接口,表示规范准备接收附加责任的对象。

    (2)ConcreteComponent(具体构建):这里指珍珠奶茶、蜂蜜奶茶等具体的奶茶。

    (3)Decorator(装饰器):定义一个与抽象构件接口一致的接口,这里表示各种口味。

    (4)ConcreteDecorator(具体装饰器):具体不同的口味,给奶茶一些附加的特色。

    现在有了这个例子,我们再回头看一看装饰模式的概念,加深理解。

    装饰器动态地给一个对象添加一些额外的功能。相比起继承子类来说,他的功能更加的灵活。不改变接口的前提下,增强所考虑的类的性能。

    什么时候我们去考虑使用装饰模式呢?下面列出了三种常见情况:

    1)需要扩展一个类的功能,或给一个类增加附加功能。

    2)需要动态的给一个对象增加功能时(可随时撤销)。

    3)类似于奶茶的例子,排列组合产生的类太多,继承不现实的情况。

    下面我们就是用代码来验证一下:

    二、代码实现装饰模式

    代码实现也比较简单,就是创建上面几个角色而已:

    第一步:定义奶茶接口(Component)

    5363575af0565e3d1ab7bb600b36c174.png

    第二步:定义两种不同种类的奶茶:珍珠奶茶和蜂蜜奶茶(ConcreteComponent)

    首先看珍珠奶茶

    5defb4130767b9ab69d8b9538986cbff.png

    接下来看蜂蜜奶茶

    f6591b3d5f1491feb3fe730f573c3db2.png

    第三步:定义口味(Decorator)

    1454e2c4573df1ed104ed2fd65524f5b.png

    第四步:具体口味(ConcreteDecorator):加冰和加咖啡

    a523ca8f1fcb1dbd7810b6a6a936cff0.png

    然后是加咖啡的:

    95bf0e6fd6d2b4761c6b87fe59943d12.png

    第五步:用户买奶茶

    389f072560f7b8b47e39d0644e02c6b0.png

    ok,以上就是装饰模式的代码实现,我们来进行一个小结:

    (1)装饰模式用于动态地给一个对象增加一些额外的职责,是一种对象结构型模式。

    (2)装饰模式包含四个角色。

    (3)比生成子类实现更为灵活。

    (4)装饰模式可分为透明装饰模式和半透明装饰模式。

    现在我们基本上对装饰模式有了基本的了解了,不过这还不够,在文中一开始我们提到过,上面的只是对透明类型的装饰模式进行了介绍,下面就来看看什么是半透明的。

    三、分析装饰模式

    1、半透明装饰模式

    在上面我们已经对透明的装饰模式进行了代码的演示,但是还需要介绍一下半透明的装饰模式。

    半透明装饰模式可以给系统带来更多的灵活性,设计相对简单,使用起来也非常方便;但是其最大的缺点在于不能实现对同一个对象的多次装饰,而且客户端需要有区别地对待装饰之前的对象和装饰之后的对象。那它是怎么实现的呢?我们来看看透明装饰模式和半透明装饰模式的区别。

    我们开的车可以定义为一个抽象构建,其具体构件可以有小轿车、越野车、跑车等等,他们都具有在马路上行驶的能力,但是突然出现了一种车,不仅能在地上跑了,也能在天上飞了,这时候我们就需要在我们的装饰器里面新增一个fly方法,过了没多久我们发现又出现了一种车更牛,不仅能在地上跑,还能在水里游了,这时候我们有需要在我们的装饰器里面新增一个swim方法。这些fly和swim是车接口没有的方法,问题就在这。也就是说果装饰角色的接口(装饰器)与抽象构件角色接口(车接口)中的方法不一致。这就是透明装饰模式和半透明装饰模式的区别。

    2、与适配器模式区别

    (1)概念区分

    适配器模式,一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。

    装饰器模式,原有的不能满足现有的需求,对原有的进行增强。

    (2)继承特点

    适配器模式是用新接口来调用原接口,原接口对新系统是不可见或者说不可用的。

    装饰者模式原封不动的使用原接口,系统对装饰的对象也通过原接口来完成使用。(增加新接口的装饰者模式可以认为是其变种--“半透明”装饰者)

    (3)包装

    装饰模式包装的是自己的兄弟类,隶属于同一个家族(相同接口或父类),

    适配器模式则修饰非血缘关系类,把一个非本家族的对象伪装成本家族的对象,注意是伪装,因此它的本质还是非相同接口的对象。

    总结:装饰模式分为透明和半透明两种,半透明要记住和适配器模式的区分。

    OK,今天的文章就先到这里。如有问题还请批评指正。

    展开全文
  • 主要介绍了java设计模式之委派模式原理分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 设计模式(Design Pattern,DP) 目录设计模式(Design Pattern,DP)设计模式的六大设计原则开闭原则:Open Closed Principle,OCP单一职责原则:Single responsibility principle,SRP里氏替换原则:Liskov ...

    设计模式(Design Pattern,DP)

    背景
    “设计模式”这个术语最初并不是出现在软件设计中,而是被用于建筑领域的设计中。 直到 1990 年,软件工程界才开始研讨设计模式的话题。 1995年,“四人组”(Gang of Four,GoF)合作出版了《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software)一书,在书籍中收录了 23 个设计模式,这是设 计模式领域里程碑的事件,导致了软件设计模式的突破。 直到今天,狭义的设计模式还是该书中所介绍的23种经典设计模式。
    概念
    软件设计模式(Software Design Pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、 代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它 是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。其目的是为了提 高代码的可重用性、代码的可读性和代码的可靠性。

    设计模式的六大设计原则

    开闭原则:Open Closed Principle,OCP

    定义:
    一个软件实体应 该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。(对扩展开放,对修改关闭)
    软件实体包括以下几个部分:
    ·项目中划分出的模块
    ·类与接口
    ·方法

    一个软件产品在她的生命周期内一般都会发生变化,开闭原则视为软件实体的未来事件而制定的对现行开发设计进 行约束的一个原则。
    案例:

    public interface IBook {
        public String getName();
        public int getPrice();
        public String getAuthor();
    }
    
    package com.java.principle.ocp;
    
    //import com.java.Design.IBook;
    
    public class NovelBook implements IBook{
        private String name;
        private int price;
        private String author;
        public NovelBook(String name, int price, String author) {
            this.name = name;
            this.price = price;
            this.author = author;
        }
        @Override
        public String getName() {
            return this.name;
        }
        @Override
        public int getPrice() {
            return this.price;
        }
        @Override
        public String getAuthor() {
            return this.author;
        }
    }
    
    import com.java.principle.ocp.IBook;
    import com.java.principle.ocp.NovelBook;
    
    import java.text.NumberFormat;
    import java.util.ArrayList;
    
    public class BookStore {
        private final static ArrayList<IBook> bookList=new ArrayList();
        static {
            bookList.add(new NovelBook("红楼梦", 9900, "曹雪芹 "));
            bookList.add(new NovelBook("侠客行", 8900, "金庸 "));
            bookList.add(new NovelBook("原则", 6900, "瑞·达利欧"));
            bookList.add(new NovelBook("海贼王", 4900, "尾田荣一郎"));
        }
        public static void main(String[] args) {
            NumberFormat format=NumberFormat.getCurrencyInstance();
            format.setMaximumFractionDigits(2);
            System.out.println("-------------书店卖书记录如下----------------------");
            for (IBook book : bookList) {
                System.out.println("书籍名称:"+book.getName()+"\t\t作者:"+book.getAuthor()+"\t\t价 格:"+format.format(book.getPrice()/100.0)+"元");
            }
        }
    }
    
    

    在这里插入图片描述

    这里是一个简单的实现书店功能的一个代码,但是如果这里需要对代码功能进行修改,比如对价格进行打折处理,按照开闭原则尽量不能对代码进行修改。我们有三种方法:
    (1)修改接口
    这样直接修改接口,添加一些抽象方法,他的实现类,以及子类,main等都会报错,都需要修改十分的麻烦,不符合开闭原则。
    (2)修改实现类
    修改实现类虽然方便,针对用户显示没有问题,但是购买书籍的人员进行修改却也显示为打折的价格,这样就存在问题。
    (3)通过扩展实现变化
    增加一个子类OffNovelBook,覆写getPrice方法,高层次的模块(也就是BookStore中static静态块中)通过 OffNovelBook类产生新的对象,完成业务变化对系统的最小开发。这样修改也少,风险也小。

    package com.java.principle.ocp;
    
    public class OffNovalBook extends NovelBook{
    
        public OffNovalBook(String name, int price, String author) {
            super(name, price, author);
        }
    
        @Override
        public int getPrice(){
            int sellPrice = super.getPrice();
            int offPrice = 0;
            if(sellPrice>7000){
                offPrice = sellPrice*90/100;
            }
            else{
                offPrice = sellPrice*80/100;
            }
            return offPrice;
        }
    }
    
    
     static {
            bookList.add(new OffNovalBook("红楼梦", 9900, "曹雪芹 "));
            bookList.add(new OffNovalBook("侠客行", 8900, "金庸 "));
            bookList.add(new OffNovalBook("原则", 6900, "瑞·达利欧"));
            bookList.add(new OffNovalBook("海贼王", 4900, "尾田荣一郎"));
        }
    

    这样再在主类中修改添加的类名,就可以实现该功能。

    作用:

    1. 对软件测试的影响
      软件遵守开闭原则的话,软件测试时只需要对扩展的代码进行测试就可以了,因为原有的测试代码仍然能够正常运 行。
    2. 可以提高代码的可复用性
      粒度越小,被复用的可能性就越大;在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性。
    3. 可以提高软件的可维护性
      遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护。

    单一职责原则:Single responsibility principle,SRP

    不能让一个对象有太多的职责,功能。如果一个对象承担了太多的责任,就会:

    1. 一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力;
    2. 当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码 的浪费

    案例:

    package com.java.principle.srp;
    
    public interface IPhone{
        //拨通电话
        public void dial(String phoneNumber);
        //通话
        public void chat(Object o);
        //通话完毕,挂断电话
        public void hangup();
    }
    
    

    修正后的案例:

    package com.java.principle.srp;
    
    public interface IPhone{
    }
    
    
    
    package com.java.principle.srp;
    
    public interface IDataTransfer extends IPhone{
        //通话
        public void chat(IConnectionManager con);
    }
    
    
    package com.java.principle.srp;
    
    public interface IConnectionManager extends IPhone{
        //拨通电话
        public void dial(String phoneNumber);
        //通话完毕,挂断电话
        public void hangup();
    }
    

    修正之后就是将一个接口拆分成3个,实现了单一原则。

    作用:
    ·降低类的复杂度。
    ·一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多。
    ·提高类的可读性。
    ·复杂性降低,自然其可读性会提高。
    ·提高系统的可维护性。可读性提高,那自然更容易维护了。
    ·变更引起的风险降低。
    ·变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可·以显著降低对其 他功能的影响。

    PS:单一职责同样也适用于方法。一个方法应该尽可能做好一件事情。如果一个方法处理的事情太多,其颗粒度会 变得很粗,不利于重用。

    人是活的,人不能被尿憋死。不一定要所有都完全遵守原则,要灵活变通。

    里氏替换原则:Liskov Substitution Principle,LSP

    子类可以替换父类。

    继承的优缺点:
    优点:

    1. 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;
    2. 提高代码的重用性;
    3. 提高代码的可扩展性,子类可形似于父类,但异于父类,保留了自己独特的个性;其实很多开源框架的扩展都 是通过继承父类实现的。
    4. 提供产品或者项目的开放性。

    缺点:

    1. 继承是侵入性的,只要继承就必须拥有父类的所有方法和属性;
    2. 降低了代码的灵活性。子类必须拥有父类的属性和方法,让子类中多了约束
    3. 增加了耦合,当父类的常量、变量或者方法被修改了,需要考虑子类的修改,所以一旦父类有了变动,很可能 会造成非常糟糕的结果,要重构大量的代码。

    定义:

    • 在一个程序中,如果可以将一个类T的对象全部替换为另一个类S的对 象,而程序的行为没有发生变化,那么S是T的子类。
    • 任何一个使用父类的地方,你都可以把它替换成它的子类,而不会发生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必可以替换。

    里氏替换原则是继承复用的基石,它为良好的继承定义了一个规范,定义中包含了4层含义:

    1. 子类必须完全实现父类的方法。
    实例:

    package com.java.principle.lsp;
    
    //枪之抽象类
    public abstract class AbstractGun {
        //射击
        public abstract void shoot();
    }
    
    
    package com.java.principle.lsp;
    
    //手枪,便于携带,但是射程8行
    public class HandGun extends AbstractGun{
        @Override
        public void shoot() {
            System.out.println("手枪之射击-----");
        }
    }
    
    
    package com.java.principle.lsp;
    
    //步枪:威力猛,皮还厚
    public class RifleGun extends AbstractGun{
        @Override
        public void shoot() {
            System.out.println("步枪射击-----");
        }
    }
    
    
    package com.java.principle.lsp;
    
    //机枪:威力大,射速快,连续射击
    public class MachineGun extends AbstractGun{
        @Override
        public void shoot() {
            System.out.println("机枪射击-----");
        }
    }
    
    
    package com.java.principle.lsp;
    
    //士兵来使用枪的角色
    public class Soilder {
        //士兵用的枪
        private AbstractGun gun;
        //给士兵哥哥配枪
        public void setGun(AbstractGun gun){
            this.gun = gun;
        }
        public void killEnemy(){
            System.out.println("士兵杀敌:");
            gun.shoot();
        }
    }
    
    
    package com.java.principle.lsp;
    
    public class Client {
        public static void main(String[] args) {
            //定义士兵
            Soilder s = new Soilder();
            s.setGun(new HandGun());
            s.killEnemy();
        }
    }
    
    

    结果图:
    在这里插入图片描述
    要点:子类实现父类的所有方法。

    2. 子类中可以增加自己特有的方法。
    案例:
    在上述代码的基础上增加了:

    package com.java.principle.lsp;
    
    //狙击步枪,有镜子之
    public class AUG extends RifleGun{
        //狙击步枪有精准的瞄准镜
        public void zoomOut(){
            System.out.println("通过四倍镜观察敌人:");
        }
    
        @Override
        public void shoot() {
            System.out.println("AUG射击之-----");
        }
    }
    
    
    package com.java.principle.lsp;
    
    public class Snipper extends Soilder{
    
    
        public void killEnemy(AUG aug) {
            //通过镜子观察
            aug.zoomOut();
            aug.shoot();
        }
    }
    
    
    package com.java.principle.lsp;
    
    public class Client {
        public static void main(String[] args) {
            //定义士兵
            Soilder s = new Soilder();
            s.setGun(new HandGun());
            s.killEnemy();
    
            //定义狙击手
            Snipper s1 = new Snipper();
            //给狙击手配枪
            s1.setGun(new AUG());
            s1.killEnemy();
        }
    }
    
    

    运行结果图:
    在这里插入图片描述
    如果这里将子类用父类替换则会报错。

    s1.setGun((AUG)(new RifleGun()));
    

    3.当子类覆盖或实现父类的方法时,方法的输入参数(方法的形参)要比父类方法的输入参数更宽松

    package com.java.principle.lsp;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class LSP {
        //当子类覆盖,重写,父类的方法的时候,方法输入参数要比父类方法输入的参数范围更大
        class Parent{
            public void fun(HashMap map) {
                System.out.println("父类执行-----");
            }
        }
        class Sub extends Parent{
            public void fun(Map map) {
                System.out.println("子类执行-----");
            }
        }
    
        public static void main(String[] args) {
            System.out.println("父类的结果:");
            LSP lsp = new LSP();
            Parent p = lsp.new Parent();
            HashMap hashMap = new HashMap();
            p.fun(hashMap);
            //父类可以被子类替换
            System.out.println("子类替换后的结果:");
            Sub s = lsp.new Sub();
            s.fun(hashMap);
        }
    }
    
    

    运行结果:
    在这里插入图片描述
    要点:当子类覆盖,重写,父类的方法的时候,方法输入参数要比父类方法输入的参数范围更大

    4. 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格
    代码部分和上部分相似:

    package com.java.principle.lsp;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class LSP1 {
        //当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
        abstract class Parent{
            public abstract Map fun();
        }
        class Sub extends Parent{
            @Override
            public HashMap fun() {
                HashMap map = new HashMap();
                map.put("b","子类被执行");
                return map;
            }
        }
    
        public static void main(String[] args) {
            LSP1 lsp = new LSP1();
            Parent p = lsp.new Sub();
            HashMap hashMap = new HashMap();
            System.out.println(p.fun());
    
        }
    }
    
    

    运行结果图:
    在这里插入图片描述
    如果反过来:

    package com.java.principle.lsp;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class LSP1 {
        //当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
        abstract class Parent{
            public abstract HashMap fun();
        }
        class Sub extends Parent{
            @Override
            public Map fun() {
                HashMap map = new HashMap();
                map.put("b","子类被执行");
                return map;
            }
        }
    
        public static void main(String[] args) {
            LSP1 lsp = new LSP1();
            Parent p = lsp.new Sub();
            HashMap hashMap = new HashMap();
            System.out.println(p.fun());
    
        }
    }
    
    

    编译无法通过。
    在这里插入图片描述

    要点:
    在这里插入图片描述
    在这里插入图片描述
    作用:

    1. 里氏替换原则是实现开闭原则的重要方式之一。
    2. 它克服了继承中重写父类造成的可复用性变差的缺点。
    3. 它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。
    • 里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
    • 如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。
    • 如果程序违背了里氏替换原则,则继承类的对象在基类出现的地方会出现运行错误。这时其修正方法是:取消原来的继承关系,重新设计它们之间的关系。

    依赖倒置原则:Dependence Inversion Principle,DIP

    定义:

    • 高层模块不应该依赖低层模块,两者都应该依赖其抽象;

    • 抽象不应该依赖细节,

    • 细节应该依赖抽象

    核心思想:要面向接口编程,不要面向实现编程。
    实例代码:

    package com.java.principle.dip;
    
    public interface ICar {
        void run();
    }
    
    
    package com.java.principle.dip;
    
    public interface IDriver {
        //驾驶车,通过传入接口实现抽象依赖关系
        void drive(ICar car);
    }
    
    
    package com.java.principle.dip;
    //司机实现司机接口
    public class Driver implements IDriver{
        public void drive(ICar car){
            System.out.println("司机在开车.");
            car.run();
        }
    }
    
    
    package com.java.principle.dip;
    //宝马车
    public class BMW implements ICar{
        @Override
        public void run(){
            System.out.println("宝马车在飞驰之.");
        }
    }
    
    
    package com.java.principle.dip;
    
    import com.java.principle.ocp.IBook;
    
    //奔驰车
    public class Benz implements ICar {
        @Override
        public void run(){
            System.out.println("奔驰车在跑.");
        }
    }
    
    
    package com.java.principle.dip;
    //属于高层模块,高层业务逻辑,对底层的依赖都建立在抽象上面
    public class Client {
        public static void main(String[] args) {
            Driver d = new Driver();
            Benz b = new Benz();
            BMW bmw = new BMW();
            d.drive(bmw);
        }
    }
    
    

    运行结果图:
    在这里插入图片描述
    按照现实生活中,司机是可以开各种各样的车的,所以在抽象中,我们具体的车就需要依赖于顶层的接口ICar这个类,进行细节上的实现。

    具体的实现方法

    1. 每个类尽量提供接口或抽象类,或者两者都具备。
    2. 变量的声明类型尽量是接口或者是抽象类。
    3. 任何类都不应该从具体类派生。
    4. 尽量不要覆写基类的方法
    5. 使用继承时结合里氏替换原则。

    作用:

    • 依赖倒置原则可以降低类间的耦合性
    • 依赖倒置原则可以提高系统的稳定性
    • 依赖倒置原则可以减少并行开发引起的风险
    • 依赖倒置原则可以提高代码的可读性和可维护性

    接口隔离原则:Interface Segregation Principle,ISP

    定义:
    要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它 的类去调用。

    接口隔离原则和单一职责都是为了提高类的内聚性降低它们之间的耦合性,体现了封装的思想,但两者是不同 的:

    • 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离
    • 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建

    实例代码:

    package com.java.principle.isp;
    
    public interface IpettyGirl {
        void goodLooking();
        void niceFigure();
        void greatTemperament();
    }
    
    
    package com.java.principle.isp;
    
    public class PettyGirl implements IpettyGirl{
        private String name;
        public PettyGirl(String name){
            this.name = name;
        }
        @Override
        public void goodLooking() {
            System.out.println(this.name+"脸好看.");
        }
    
        @Override
        public void niceFigure() {
            System.out.println(this.name+"身材无敌.");
        }
    
        @Override
        public void greatTemperament() {
            System.out.println(this.name+"气质好.");
        }
    
    }
    
    
    package com.java.principle.isp;
    
    public abstract class AbstractSearcher {
        public IpettyGirl girl;
    
        public AbstractSearcher(IpettyGirl girl1){
            this.girl = girl;
        }
    
        public abstract void seqarch();
    }
    
    
    package com.java.principle.isp;
    
    public class Searcher extends AbstractSearcher {
        public Searcher(IpettyGirl girl) {
            super(girl);
        }
    
    
        @Override
        public void search() {
            System.out.println("找到的美女如下:");
            super.girl.goodLooking();
            super.girl.niceFigure();
            super.girl.greatTemperament();
        }
    }
    
    
    package com.java.principle.isp;
    
    public class Client {
        //定义美女
        IpettyGirl reBa = new PettyGirl("迪丽热巴");
        AbstractSearcher searcher = new Searcher(reBa);
        searcher.search();
    }
    
    

    但是一个人如果气质很好,身材与长相平平,那也可以称之为美女,这个时候代码无法完成需求,所以则需要将接口进行拆分

    package com.java.principle.isp;
    
    public interface IGoodBodyGirl {
        void goodLooking();
        void niceFigure();
    }
    
    
    package com.java.principle.isp;
    
    public interface IGreatTempermentGirl {
        void greatTemperment();
    }
    
    

    这样就可以完成。
    具体的实现方法:

    • 接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。
    • 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
    • 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑。
    • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

    作用:

    1. 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
    2. 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
    3. 如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
    4. 使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
    5. 能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫 设计冗余的代码。

    迪米特法则:Law of Demeter,LoD

    定义:
    要求一个对象应该对其他对象有最少的了解。通俗的说,一个类应该对自己需要耦合或调用的类知道的最少,被耦合或调用的类的内部是如何复杂都与我无关,我就知道你提供的public方法就好
    只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

    朋友:

    1. 当前对象本身(this)
    2. 当前对象的方法参数(以参数形式传入到当前对象方法中的对象)
    3. 当前对象的成员对象
    4. 如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友
    5. 当前对象所创建的对象

    演示代码:

    package com.java.principle.lod;
    
    public class Client {
        public static void main(String[] args) {
            Agent agent = new Agent();
            agent.setStar(new Star("吴京"));
            agent.setFans(new Fans("小李"));
            agent.meeting();
            agent.setCompany(new Company("中央电视台"));
            agent.business();
        }
    }
    class Agent{
        private Star star;
        private Fans fans;
        private Company company;
    
        public void meeting(){
            System.out.println(this.fans.getName()+"与明星"+this.star.getName()+"见面了.");
        }
        public void business(){
            System.out.println(this.company.getName()+"与明星"+this.star.getName()+"合作了.");
        }
        public void setCompany(Company company) {
            this.company = company;
        }
    
        public void setFans(Fans fans) {
            this.fans = fans;
        }
    
        public void setStar(Star star) {
            this.star = star;
        }
    }
    class Star{
        private String name;
        public Star(String name){
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    }
    
    class Fans{
        private String name;
    
        public Fans(String name){
            this.name = name;
        }
        public String getName(){
            return name;
        }
    }
    
    class Company{
        private String name;
    
        public Company(String name){
            this.name = name;
        }
        public String getName(){
            return name;
        }
    }
    

    运行截图:
    在这里插入图片描述
    明星与粉丝,明星与公司,都是通过经纪人这个“朋友”进行交流。
    优点:

    1. 降低了类之间的耦合度,提高了模块的相对独立性。
    2. 由于亲合度降低,从而提高了类的可复用率和系统的扩展性

    但是,过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

    实现方法:

    1. 从依赖者的角度来说,只依赖应该依赖的对象。
    2. 从被依赖者的角度说,只暴露应该暴露的方法。

    注意:

    • 在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。
    • 在类的结构设计上,尽量降低类成员的访问权限。
    • 在类的设计上,优先考虑将一个类设置成不变类。
    • 在对其他类的引用上,将引用其他对象的次数降到最低。
    • 不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。
    • 谨慎使用序列化(Serializable)功能。

    设计模式具体之

    在这里插入图片描述

    设计模式分为创建型,行为型,结构型。

    创建型

    定义:
    创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。就像我们去商场购买商品时,不需要知道商品是怎么生产出来一样,因为它们由专门的厂商生产。

    单例模式

    概念:
    中国自从秦始皇确立了皇帝这个职位之后,同一个时期基本上上就只有一个人孤零零的坐在皇位上啦。这种情况的 好处就是大家好办事,大家讨论或者汇报大事的时候只要提及皇帝,每个人都知道指的是谁,不需要在皇帝面前加 上特定的称呼。这种过程反应到软件设计领域就是:一个类只能产生一个对象(皇帝),大家对他的依赖都是相同 的。我们把皇帝这种特殊的职位通过程序来实现。

    代码实例:

    package com.java.dp.singleton;
    
    public class Emperor {
        private static Emperor emperor = null;
        //构造函数私有,避免在外面随意创建
        private Emperor(){}
        //提供产生实例的方法
        public static Emperor getInstance(){
            if(emperor==null){
                emperor = new Emperor();
            }
            return emperor;
        }
        public void work(){
            System.out.println("我是大王康曦,有事说事.");
        }
    }
    
    
    package com.java.dp.singleton;
    //大臣
    public class Minister {
        public static void main(String[] args) {
            for(int i = 1;i <= 7;i++){
                Emperor emperor = Emperor.getInstance();
                System.out.print("第"+i+"天:");
                emperor.work();
            }
        }
    
    }
    
    

    运行结果:
    在这里插入图片描述
    核心操作:对构造函数进行私有,外部无法创造实例对象,只能通过内部进行创建。
    在这里插入图片描述
    该代码存在线程不安全的情况,如果线程A刚好进行到判断的时候,B创建完成,就会出现不安全的问题。
    解决代码:
    1.加关键词synchronized
    在这里插入图片描述
    2.加载类直接创建

    package com.java.dp.singleton;
    
    public class Emperor {
        private static Emperor emperor = new Emperor();
        //构造函数私有,避免在外面随意创建
        private Emperor(){}
        //提供产生实例的方法
        public static Emperor getInstance(){
            return emperor;
        }
        public void work(){
            System.out.println("我是大王康曦,有事说事.");
        }
    }
    
    

    使用单例模式场景:

    • 某类只要求生成一个对象的时候,如一个航班的机长、每个人的身份证号等。

    • 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。

    • 当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。

    • 在计算机系统中, Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、

    • 打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应

    • 用程序中的对话框、系统中的缓存等常常被设计成单例。

    优点:

    • 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
    • 避免对资源的多重占用(比如写文件操作)。
    • 单例模式可以在系统设置全局的访问点,优化和共享资源访问。

    缺点:

    • 单例模式一般没有接口,扩展很困难。如果要扩展,只能修改代码。

    • 与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

    若需要取得几个对象,可通过内部创建ArrayList进行存储。

    package com.java.dp.singleton;
    
    import java.util.ArrayList;
    import java.util.Random;
    
    public class Emperor {
        private String name;
        private static final int maxNum = 2;
        private static ArrayList<Emperor> list = new ArrayList<>(2);
        static {
    
            list.add(new Emperor("皇帝:康熙"));
            list.add(new Emperor("皇帝:乾隆"));
        }
        private static Emperor emperor = new Emperor();
        //构造函数私有,避免在外面随意创建
        private Emperor(){}
        private Emperor(String name){
            this.name = name;
        }
        //提供产生实例的方法
        public static Emperor getInstance(){
            int index = new Random().nextInt(maxNum);
            return list.get(index);
        }
        public void work(){
            System.out.println("我是大王"+name+",有事说事.");
        }
    }
    
    

    在这里插入图片描述

    工厂方法模式

    定义:
    定义一个用户创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

    使用场景:

    • 客户只知道创建产品的工厂名,而不知道具体的产品名。 如 TCL 电视工厂、海信电视工厂等。
    • 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
    • 客户不关心创建产品的细节,只关心产品的品牌。

    代码模板:

    //抽象产品:提供了产品的接口 
    public interface Product{ 
    	public void method(); 
    }
    //具体的产品可以有多个,都实现抽象产品接口 
    public class ConcreteProduct1 implements Product{ 
    	public void method(){ 
    		//具体业务逻辑处理,例如 
    		System.out.println("具体产品1显示..."); 
    	} 
    }
    public class ConcreteProduct2 implements Product{ 
    	public void method(){ 
    		//具体业务逻辑处理,例如 
    		System.out.println("具体产品2显示..."); 
    	} 
    }
    //抽象工厂:负责定义产品对象的产生 
    public abstract class AbstractFactory{ 
    	//创建一个产品对象,输入的参数类型可以自行设置 
    	public abstract <T extends Product>T createProduct(Class<T> tClass); 
    }
    //具体工厂:具体如何生产一个产品的对象,是由具体的工厂类实现的 
    public class ConcreteFactory implements AbstractFactory{ 
    	public <T extends Product> T createProduct(Class<T> tClass) { 
    	Product product=null; 
    	try {
    		product=(T)Class.forName(tClass.getName()).newInstance(); 
    	} catch (Exception e) { 
    	//异常处理 
    	}
    	return (T)product; 
    } 
    }
    //场景类: 
    public class Client { 
    	public static void main(String[] args) { 
    		AbstractFactory factory=new ConcreteFactory(); 
    		Product product=factory.createProduct(ConcreteProduct1.class); 
    //继续其他业务处理 
    	} 
    }
    
    

    面包接口:

    package com.java.dp.factory;
    
    public interface bread {
        //每个面包都有颜色
        void getColor();
    
    }
    
    

    北极熊面包:

    package com.java.dp.factory;
    
    public class PolarBearBread implements bread{
    
        @Override
        public void getColor() {
            System.out.println("烤的时间短了---北极熊面包");
        }
    }
    
    

    黑熊面包:

    package com.java.dp.factory;
    
    public class BlackBearBread implements bread{
    
        @Override
        public void getColor() {
            System.out.println("烤的时间长了---黑熊面包");
        }
    }
    
    

    布朗熊面包:

    package com.java.dp.factory;
    
    public class BrownBearBread implements bread{
    
        @Override
        public void getColor() {
            System.out.println("烤的时间OK---布朗熊面包");
        }
    }
    
    

    抽象面包创建工厂:

    package com.java.dp.factory;
    
    public abstract class AbstractBreadFactory {
        public abstract bread createBread(Class cls);
    
    }
    
    

    具体面包创建工厂:

    package com.java.dp.factory;
    
    public class BreadFactory extends AbstractBreadFactory{
    
        @Override
        public bread createBread(Class cls) {
            bread b = null;
            try {
                b = (bread) Class.forName(cls.getName()).newInstance();
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("出错了.");
            }
            return b;
        }
    }
    
    

    大厨:

    package com.java.dp.factory;
    
    public class Chef {
        public static void main(String[] args) {
            //烤箱OK
            AbstractBreadFactory abf = new BreadFactory();
            //1
            System.out.println("厨师第一次烤");
            bread polorBearBread = abf.createBread(PolarBearBread.class);
            polorBearBread.getColor();
            //2
            System.out.println("厨师第二次烤");
            bread blackBearBread = abf.createBread(BlackBearBread.class);
            blackBearBread.getColor();
            //3
            System.out.println("厨师第三次烤");
            bread brownBearBread = abf.createBread(BrownBearBread.class);
            brownBearBread.getColor();
        }
    }
    
    

    运行结果:
    在这里插入图片描述

    抽象工厂模式

    抽象工厂模式和工厂模式很相似,主要的区别还是生产多种类的产品。

    • 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
    • 系统一次只可能消费其中某一族产品,即同族的产品一起使用。

    定义:
    为创建一组相关或者相互依赖的对象提供一个接口,而且无须指定他们 的具体类。
    通用代码:

    //抽象产品类(只写了一个AbstractProductA,AbstractProductB省略): 
    public abstract class AbstractProductA{ 
    	//每个产品的共有方法 
    	public void sharedMthod(){ 
    	}
    	//每个产品相同方法,不同实现 
    	public abstract void doSomething(); 
    }
    具体产品类: 
    public class ProductA1 extends AbstractProductA{ 
    	public abstract void doSomething(){ 
    		System.out.println("产品A1的实现方法");
    	} 
    }
    public class ProductA2 extends AbstractProductA{ 
    	public abstract void doSomething(){ 
    		System.out.println("产品A2的实现方法"); 
    	} 
    }
    //抽象工厂类: 
    public abstract class AbstractCreator{ 
    	//创建A产品家族 
    	public abstract AbstractProductA createProductA(); 
    	//创建B产品家族 
    	public abstract AbstractProductB createProductB(); 
    	//如果有N个产品族,该类中应该有N个创建方法 
    }
    //产品等级实现类: 
    //有M个产品等级就应该有M个工厂的实现类,在每个实现工厂中,实现不同产品族的生产业务。 
    public class Creator1 extends AbstractCreator{ 
    	//只生成产品等级为1的A产品 
    	public AbstractProductA createProductA(){ 
    		return new ProductA1(); 
    	}
    	//只生成产品等级为1的B产品 
    	public AbstractProductB createProductB(){ 
    		return new ProductB1(); 
    	} 
    }
    public class Creator2 extends AbstractCreator{ 
    	//只生成产品等级为2的A产品 
    	public AbstractProductA createProductA(){ 
    		return new ProductA2(); 
    	}
    	//只生成产品等级为2的B产品 
    	public AbstractProductB createProductB(){ 
    		return new ProductB2(); 
    	} 
    }
    //场景类 
    public class Client{ 
    	public static void main(String[]] args){ 
    		//定义两个工厂 
    		AbstractCreator creator1=new Creator1(); 
    		AbstractCreator creator2=new Creator2(); 
    		//产生A1对象 
    		AbstractProductA a1=creator1.createProductA(); 
    		//产生A2对象 
    		AbstractProductA a2=creator2.createProductA(); 
    		//产生B1对象 
    		AbstractProductA a1=creator1.createProductB(); 
    		//产生B2对象 
    		AbstractProductA a2=creator2.createProductB(); 
    		//按需求自己实现其他 
    	}
    }
    

    实例代码:

    package com.java.dp.abstractfactory;
    
    public interface bread {
        //每个面包都有颜色
        void getColor();
        //面包都有不同的馅
        void getType();
    }
    
    
    package com.java.dp.abstractfactory;
    
    public interface BreadFactory {
        //创建北极熊面包
        bread createPolorBearBread();
        //创建黑熊面包
        bread createBlackBearBread();
        //创建布朗熊面包
        bread createBrownBearBread();
    }
    
    
    package com.java.dp.abstractfactory;
    
    public abstract class AbstractBlackBearBread implements bread {
    
        @Override
        public void getColor() {
            System.out.println("烤的时间长了---黑熊面包");
        }
    
    }
    
    
    package com.java.dp.abstractfactory;
    
    public abstract class AbstractBrownBearBread implements bread {
    
        @Override
        public void getColor() {
            System.out.println("烤的时间OK---布朗熊面包");
        }
    }
    
    
    package com.java.dp.abstractfactory;
    
    public abstract class AbstractPolarBearBread implements bread {
    
        @Override
        public void getColor() {
            System.out.println("烤的时间短了---北极熊面包");
        }
    }
    
    
    package com.java.dp.abstractfactory;
    
    public class MangoBlackBearBread extends AbstractBlackBearBread {
        @Override
        public void getType() {
            System.out.println("黑熊面包---芒果馅儿");
        }
    }
    
    
    package com.java.dp.abstractfactory;
    
    public class MangoBrownBearBread extends AbstractBrownBearBread {
        @Override
        public void getType() {
            System.out.println("布朗熊---芒果馅儿");
        }
    }
    
    
    package com.java.dp.abstractfactory;
    
    public class MangoFactory implements BreadFactory{
        @Override
        public bread createPolorBearBread() {
            return new MangoPolorBearBread();
        }
    
        @Override
        public bread createBlackBearBread() {
            return new MangoBlackBearBread();
        }
    
        @Override
        public bread createBrownBearBread() {
            return new MangoBrownBearBread();
        }
    }
    
    
    package com.java.dp.abstractfactory;
    
    public class MangoPolorBearBread extends AbstractPolarBearBread {
        @Override
        public void getType() {
            System.out.println("北极熊---芒果馅");
        }
    }
    
    
    package com.java.dp.abstractfactory;
    
    public class AppleBlackBearBread extends AbstractBlackBearBread {
        @Override
        public void getType() {
            System.out.println("黑熊面包---苹果馅儿");
        }
    }
    
    
    package com.java.dp.abstractfactory;
    
    public class AppleBrownBearBread extends AbstractBrownBearBread {
        @Override
        public void getType() {
            System.out.println("布朗熊---苹果馅儿");
        }
    }
    
    
    package com.java.dp.abstractfactory;
    
    public class AppleFactory implements BreadFactory{
        @Override
        public bread createPolorBearBread() {
            return new ApplePolorBearBread();
        }
    
        @Override
        public bread createBlackBearBread() {
            return new ApplePolorBearBread();
        }
    
        @Override
        public bread createBrownBearBread() {
            return new ApplePolorBearBread();
        }
    }
    
    
    package com.java.dp.abstractfactory;
    
    public class ApplePolorBearBread extends AbstractPolarBearBread {
        @Override
        public void getType() {
            System.out.println("北极熊---苹果馅");
        }
    }
    
    
    package com.java.dp.abstractfactory;
    
    public class Client {
        public static void main(String[] args) {
            //Apple馅儿的烤面包
            BreadFactory apple = new AppleFactory();
            //芒果馅儿的烤箱
            BreadFactory mango = new MangoFactory();
            //生产之
            System.out.println("第一批");
            bread b1 = apple.createBlackBearBread();
            bread b2 = apple.createBrownBearBread();
            bread b3 = mango.createBlackBearBread();
            b1.getColor();
            b1.getType();
            b2.getColor();
            b2.getType();
            b3.getColor();
            b3.getType();
            System.out.println("第二批");
            bread b4 = mango.createBrownBearBread();
            bread b5 = apple.createPolorBearBread();
            b4.getColor();
            b4.getType();
            b5.getColor();
            b5.getType();
    
        }
    }
    
    

    运行结果图:
    在这里插入图片描述
    相比于之前的工厂模式,他能够生产出各种各类的种类。当我们需要生产类的属性有各种各样的时候可以采用抽象工厂模式。主要的思想还是抽象类与接口的应用。

    优点:

    • 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
    • 当增加一个新的产品族时不需要修改原代码,满足开闭原则。

    缺点:

    • 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。

    应用场景:

    1. 适合于产品之间相互关联、相互依赖且相互约束的地方
    2. 需要动态切换产品族的地方

    建造者(Builder)模式

    定义:
    指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者 模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组 成部分是不变的,但每一部分是可以灵活选择的。
    模式的结构

    1. 产品(Product)类:它是包含多个组成部件的复杂对象,由具体建造者来创建其各个部件。
    2. 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
    3. 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
    4. 导演(Director)类:它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息

    模板代码:
    产品(Product)类:

    public class Product { 
    	private String partA; 
    	private String partB; 
    	private String partC; 
    	public void setPartA(String partA){ 
    		this.partA=partA; 
    	}
    	public void setPartB(String partB){ 
    		this.partB=partB; 
    	}
    	public void setPartC(String partC){ 
    		this.partC=partC; 
    	}
    	public void doSomething(){ 
    		//独立业务处理 
    	} 
    }
    
    

    抽象建造者(Builder):

    public abstract class Builder{ 
    	//创建产品的不同部分,以获取不同产品 
    	public abstract void buildPartA(); 
    	public abstract void buildPartB(); 
    	public abstract void buildPartC(); 
    	//返回产品对象 
    	public abstract Product buildProduct(); 
    }
    
    

    具体建造者:

    public class ConcreteBuilder extends Builder{ 
    	private Product product=new Product(); 
    	public void buildPartA(){ 
    		product.setPartA("建造 PartA"); 
    	}
    	public void buildPartB(){ 
    		product.setPartA("建造 PartB"); 
    	}
    	public void buildPartC(){
    		product.setPartA("建造 PartC"); 
    	}
    	//组件一个产品 
    	public Product buildProduct(){ 
    		return product; 
    	} 
    }
    
    

    指挥者:

    public class Director { 
    	private Builder builder; 
    	public Director(Builder builder){ 
    		this.builder=builder; 
    	}
    	//产品构建与组装方法:设置不同的零件,生成不同的产品 
    	public Product constructA(){ 
    		builder.buildPartA(); 
    		builder.buildPartB(); 
    		builder.buildPartC(); 
    		return builder.buildProduct(); 
    	}
    	public Product constructB(){ 
    		builder.buildPartB(); 
    		builder.buildPartA(); 
    		builder.buildPartC(); 
    		return builder.buildProduct(); 
    	} 
    }
    
    

    场景类:

    public class Client{ 
    	public static void main(String[] args){ 
    		Builder builder=new ConcreteBuilder(); 
    		Director director=new Director(builder); 
    		Product product=director.construct(); 
    		product.doSomething(); 
    	} 
    }
    
    

    实例代码:
    我们需要构建车的类,有各种品牌的车,宝马,奔驰等,每辆车有不同的功能,启动,鸣笛等,有一定的顺序要求。

    package com.java.dp.builder;
    
    import java.util.ArrayList;
    
    public abstract class CarBuilder {
        public abstract void setSequence(ArrayList<String> sequence);
        public abstract CarModel getCarModel();
    }
    
    
    package com.java.dp.builder;
    
    import java.util.ArrayList;
    
    public abstract class CarModel {
        private ArrayList<String> sequence = new ArrayList<>();
        public void setSequence(ArrayList<String> sequence){
            this.sequence = sequence;
        }
        protected abstract void start();
        protected abstract void stop();
        protected abstract void alarm();
        protected abstract void engineBoom();
    
        public void run(){
            for (String s : sequence) {
                if("start".equalsIgnoreCase(s)){
                    this.start();
                }else if ("stop".equalsIgnoreCase(s)){
                    this.stop();
                }else if ("alarm".equalsIgnoreCase(s)){
                    this.alarm();
                }else if ("engineBoom".equalsIgnoreCase(s)){
                    this.engineBoom();
                }
            }
        }
    }
    
    
    package com.java.dp.builder;
    
    public class Benz extends CarModel{
        @Override
        protected void start() {
            System.out.println("奔驰车---启动之");
        }
    
        @Override
        protected void stop() {
            System.out.println("奔驰车---停车之");
        }
    
        @Override
        protected void alarm() {
            System.out.println("奔驰车---鸣笛之");
        }
    
        @Override
        protected void engineBoom() {
            System.out.println("奔驰车---引擎轰鸣之");
        }
    }
    
    
    package com.java.dp.builder;
    
    public class BMW extends CarModel{
        @Override
        protected void start() {
            System.out.println("宝马车---启动之");
        }
    
        @Override
        protected void stop() {
            System.out.println("宝马车---停车之");
        }
    
        @Override
        protected void alarm() {
            System.out.println("宝马车---鸣笛之");
        }
    
        @Override
        protected void engineBoom() {
            System.out.println("宝马车---引擎轰鸣之");
        }
    }
    
    
    package com.java.dp.builder;
    
    import java.util.ArrayList;
    
    public class BenzBuilder extends CarBuilder{
        private Benz benz = new Benz();
        @Override
        public void setSequence(ArrayList<String> sequence) {
            this.benz.setSequence(sequence);
        }
    
        @Override
        public CarModel getCarModel() {
            return benz;
        }
    }
    
    
    package com.java.dp.builder;
    
    import java.util.ArrayList;
    
    public class BMWBuilder extends CarBuilder{
        private BMW bmw = new BMW();
        @Override
        public void setSequence(ArrayList<String> sequence) {
            this.bmw.setSequence(sequence);
        }
    
        @Override
        public CarModel getCarModel() {
            return bmw;
        }
    }
    
    
    package com.java.dp.builder;
    
    import java.util.ArrayList;
    
    public class Director {
        private ArrayList<String> sequence = new ArrayList<>();
        private BenzBuilder benzBuilder = new BenzBuilder();
        private BMWBuilder bmwBuilder = new BMWBuilder();
    
        /**
         * 奔驰车A
         */
        public Benz getABenz() {
            this.sequence.clear();
            this.sequence.add("start");
            this.sequence.add("stop");
            this.benzBuilder.setSequence(sequence);
            return (Benz) this.benzBuilder.getCarModel();
        }
        /**
         * 奔驰车B
         */
        public Benz getBBenz() {
            this.sequence.clear();
            this.sequence.add("engineBoom");
            this.sequence.add("start");
            this.sequence.add("stop");
            this.benzBuilder.setSequence(sequence);
            return (Benz) this.benzBuilder.getCarModel();
        }
    
        /**
         * 宝马车
         */
        public BMW getBMW() {
            this.sequence.clear();
            this.sequence.add("engineBoom");
            this.sequence.add("start");
            this.sequence.add("stop");
            this.bmwBuilder.setSequence(sequence);
            return (BMW) this.bmwBuilder.getCarModel();
        }
    }
    
    package com.java.dp.builder;
    
    import java.util.ArrayList;
    
    public class Client {
        public static void main(String[] args) {
            Director director = new Director();
            //奔驰A
            System.out.println("奔驰A");
            director.getABenz().run();
            //奔驰B
            System.out.println("奔驰B");
            director.getBBenz().run();
            //宝马
            System.out.println("宝马C");
            director.getBMW().run();
        }
    }
    
    

    运行结果:
    在这里插入图片描述
    优点:

    • 各个具体的建造者相互独立,有利于系统的扩展。
    • 客户端不必知道产品内部组成的细节,便于控制细节风险。

    缺点:

    • 产品的组成部分必须相同,这限制了其使用范围。
    • 如果产品的内部变化复杂,该模式会增加很多的建造者类。

    应用场景
    建造者(Builder)模式创建的是复杂对象,其产品的各个部分经常面临着剧烈的变化,但将它们组合在一起的算 法却相对稳定,所以它通常在以下场合使用。

    • 相同的方法,不同的执行顺序,产生不同的实践结果
    • 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
    • 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立 的

    结构型

    代理(Proxy)模式

    代理模式可以理解为代练,一个代练类对核心类进行一些操作。
    定义:
    为其他对象提供一种代理以控制这个对象的访问。这是一个使用频率非常高的模式。

    模式的结构:

    1. 抽象主题(Subject)角色:抽象主题类可以是接口或抽象类,是一个普通的业务类型定义,声明真实主题和 代理对象实现的业务方法,无特殊要求。
    2. 真实主题(Real Subject)角色:真实主题角色类也叫作被委托角色、被代理角色,实现了抽象主题中的具体 业务,是代理对象所代表的真实对象,是最终要引用的对象,是业务逻辑的具体执行者。
    3. 代理(Proxy)角色:也叫做委托类、代理类。他负责对真实角色的应用,把所有抽象主题类定义的方法限制 委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后的工作。提供了与真实主题相 同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

    模板代码:

    //抽象主题类 
    public interface Subject { 
    	void request(); 
    }
    
    //真实主题类 
    public class RealSubject implements Subject{ 
    	public void request(){ 
    		//业务逻辑处理 
    	} 
    }
    
    //代理类:代理模式的核心就在代理类上 
    public class Proxy implements Subject{ 
    	//要代理哪个实现类 
    	private Subject subject=null; 
    	//通过构造方法传入被代理对象(也可以有其他方式) 
    	public Proxy(Subject subject){ 
    		this.subject=subject; 
    	}
    	public void request(){ 
    		preRequest(); 
    		resubjectalSubject.request(); 
    		postRequest(); 
    	}
    	//预处理 
    	public void preRequest(){ 
    		System.out.println("访问真实主题之前的预处理。"); 
    	}
    	//善后工作 
    	public void postRequest(){ 
    		System.out.println("访问真实主题之后的善后。"); 
    	} 
    }
    
    //场景类 
    public class Client { 
    	public static void main(String[] args){ 
    		Proxy proxy=new Proxy(); 
    		proxy.request(); 
    	} 
    }
    

    实例代码:

    package com.java.dp.proxy;
    
    public interface IGamePlayer {
        void login(String username,String password);
        void killBoss();
        void upgrade();
    }
    
    
    package com.java.dp.proxy;
    
    public class GamePlayer implements IGamePlayer{
        private String petName = "";
        public GamePlayer(String patname){
            this.petName = patname;
        }
        @Override
        public void login(String username, String password) {
            System.out.println("登录账号:"+username+",昵称:"+petName+"登录成功!");
        }
    
        @Override
        public void killBoss() {
            System.out.println("打怪ing-----");
        }
    
        @Override
        public void upgrade() {
            System.out.println(this.petName+"升级成功!");
        }
    }
    
    
    package com.java.dp.proxy;
    
    public class GamePlayerProxy implements IGamePlayer{
        private IGamePlayer player = null;
        public GamePlayerProxy(IGamePlayer player){
            this.player = player;
        }
        @Override
        public void login(String username, String password) {
            this.player.login(username,password);
        }
    
        @Override
        public void killBoss() {
            this.player.killBoss();
        }
    
        @Override
        public void upgrade() {
            this.player.upgrade();
        }
    }
    
    
    package com.java.dp.proxy;
    
    public class Client {
        public static void main(String[] args) {
            //定义玩家
            IGamePlayer player = new GamePlayer("残影");
            //代练者
            IGamePlayer proxyPlayer = new GamePlayerProxy(player);
            //打游戏
            System.out.println("开始时间2020-2-2 13:30");
            proxyPlayer.login("cy","123456");
            proxyPlayer.upgrade();
            System.out.println("结束时间2020-2-2 14:40");
        }
    }
    
    

    运行结果图:
    在这里插入图片描述
    最后的结果图不论是proxyPlayer还是player结果显示都是一样的,但是内部确是不一样的。核心思想是代理类和核心类继承了一样的接口,但是代理内部的方法都是调用了核心内部中的方法

    优点:

    • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
    • 代理对象可以扩展目标对象的功能;
    • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;

    缺点:

    • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
    • 增加了系统的复杂度;

    适配器(Adapter)模式

    定义:
    将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
    适配器模式分为类结构型模式对象结构型模式两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件 库中的相关组件的内部结构,所以应用相对较少些。
    在这里插入图片描述
    在这里插入图片描述
    模式的结构

    1. 目标(Target)角色:该角色定义把其他类转换为何种接口,也就是我们的期望接口。它可以是抽象类或接 口。
    2. 源(Adaptee)角色:你想把谁转换为目标角色,这个“谁”就是源角色,他是已经存在的、运行良好的类或者 对象,经过适配器角色的包装,他会成为一个新的角色。
    3. 适配器(Adapter)角色:是适配器模式的核心橘色,其他两个角色都是已经存在的角色,而适配器角色是需 要新建立的,他的职责很简单:把原角色转换为目标角色。如何转换?通过继承或者类关联的方式。

    模板代码:

    //目标角色 
    public interface Target{ 
    	public void request();
    }
    
    //源角色 
    public class Adaptee{ 
    	public void doSomething() { 
    		System.out.println("源角色的----doSomething!"); 
    	} 
    }
    
    //适配器角色 
    public class Adapter extends Adaptee implements Target{ 
    	public void request() { 
    		super.doSomething(); 
    	} 
    }
    
    //场景类: 
    public class Client{ 
    	public static void main(String[] args) { 
    		Target target = new Adapter(); 
    		target.request(); 
    	} 
    }
    

    实例代码:
    通过手机三孔和两孔的插头,以及转接头的例子来理解适配器模式。

    package com.java.dp.adaptor;
    
    public interface ThreePower {
        void powerByThree();
    }
    
    
    package com.java.dp.adaptor;
    
    public class TwoPower {
        public void powerByTwo(){
            System.out.println("两圆孔的插座.");
        }
    }
    
    
    package com.java.dp.adaptor;
    
    public class TwoToThreeAdaptor implements ThreePower{
        private TwoPower twoPower;
        public TwoToThreeAdaptor(TwoPower twoPower){
            this.twoPower = twoPower;
        }
        @Override
        public void powerByThree() {
            twoPower.powerByTwo();
            System.out.println("适配器转换为三孔。");
        }
    }
    
    
    package com.java.dp.adaptor;
    
    public class Phone {
        private ThreePower threePower;
        public Phone(ThreePower threePower){
            this.threePower = threePower;
        }
    
        public void rechange(){
            threePower.powerByThree();
            System.out.println("手机用三孔充电.");
        }
        public void photo(){
            System.out.println("手机拍照.");
        }
    }
    
    
    package com.java.dp.adaptor;
    
    public class Client {
    
        public static void main(String[] args) {
            //只有一个两孔的插座
            TwoPower twoPower = new TwoPower();
            //通过适配器转换
            ThreePower threePower = new TwoToThreeAdaptor(twoPower);
            //手机
            Phone phone = new Phone(threePower);
            phone.rechange();
            phone.photo();
    
        }
    }
    
    

    优点:

    • 客户端通过适配器可以透明地调用目标接口。
    • 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
    • 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。

    缺点:

    • 对类适配器来说,更换适配器的实现过程比较复杂。

    适配器模式是一个补偿模式,或者说是一个“补救”模式。通常用来解决接口不相容的问题。一般项目开始的时候用的偏 少。大多数是在项目的需求不断变化的时候,技术为了业务服务的,因此业务在变化的时候,对技术也提出了要求,这些 时候可能就需要这样的补救模式诞生。

    装饰(Decorator)模式

    定义:
    指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

    通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增 多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的 类结构不变的前提下,为其提供额外的功能,这就是装饰模式的目标。

    模式结构:

    1. 抽象构件(Component)角色:是一个抽象类或者接口,定义最核心的对象,也就是最原始的对象。
    2. 具体构件(Concrete Component)角色:实现抽象构件,通过装饰角色为其添加一些职责。
    3. 抽象装饰(Decorator)角色:一般是一个抽象类,继承抽象构件,实现其抽象方法,里面不一定有抽象的方 法,在他的属性里一般都会有一个private变量指向Component抽象构件 。
    4. 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

    实例代码:

    package com.java.dp.decorator;
    
    public abstract class ScoreReport {
        public abstract void show();
        public abstract void sign(String name);
    }
    
    
    package com.java.dp.decorator;
    
    public class Decorator extends ScoreReport{
        private ScoreReport report;
        public Decorator(ScoreReport scoreReport){
            this.report = scoreReport;
        }
        @Override
        public void show() {
            this.report.show();
        }
    
        @Override
        public void sign(String name) {
            this.report.sign(name);
        }
    }
    
    
    package com.java.dp.decorator;
    
    public class MyScoreReport extends ScoreReport{
    
        @Override
        public void show() {
            System.out.println("尊敬的家长:");
            System.out.println("\t以下是你孩子本次考试成绩,请阅读后在后面签名:");
            System.out.println("\t语文 60 数学61 自然90");
            System.out.println("\t\t\t家长签名:");
        }
    
        @Override
        public void sign(String name) {
            System.out.println("\t\t\t家长签名:"+name);
        }
    }
    
    
    package com.java.dp.decorator;
    
    public class SortDecorator extends Decorator{
        public SortDecorator(ScoreReport scoreReport) {
            super(scoreReport);
        }
        public void reportSort(){
            System.out.println("排名:23");
        }
    
        @Override
        public void show() {
            super.show();
            this.reportSort();
        }
    }
    
    
    package com.java.dp.decorator;
    
    public class MaxScoreDecorator extends Decorator{
        public MaxScoreDecorator(ScoreReport scoreReport) {
            super(scoreReport);
        }
        public void reportMaxScore(){
            System.out.println("这次考试最高的成绩:语文78 数学79 自然100");
        }
    
        @Override
        public void show() {
            this.reportMaxScore();
            super.show();
        }
    }
    
    
    package com.java.dp.decorator;
    
    public class Parent {
        public static void main(String[] args) {
            //拿到原始成绩单
            ScoreReport report = new MyScoreReport();
            //加了最高成绩修饰的成绩单
            report = new MaxScoreDecorator(report);
            report.show();
            report.sign("56");
        }
    }
    
    

    在这里插入图片描述
    优点:

    • 采用装饰模式扩展对象的功能比采用继承方式更加灵活。
    • 可以设计出多个不同的具体装饰类,创造出多个不同行为的组合

    缺点:

    • 装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。

    应用:
    装饰模式在 java语言中的最著名的就是 Java I/O 标准库的设计了。 例如,InputStream 的子类 FilterInputStream,OutputStream 的子类 FilterOutputStream,Reader 的子类 BufferedReader 以及 FilterReader,还有 Writer 的子类 BufferedWriter、FilterWriter 以及 PrintWriter 等,它们都是抽象装饰类。

    行为型

    策略模式(Strategy)

    结果一样,方式不一样。
    定义:
    该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客 户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不 同的对象对这些算法进行管理。
    结构:

    1. 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用 这个接口调用不同的算法,一般使用接口或抽象类实现。
    2. 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
    3. 策略上下文角色StrategyContext:策略上下文,负责和具体的策略实现交互,通常策略上下文对象会持有一个 真正的策略实现对象,策略上下文还可以让具体的策略实现从其中获取相关数据,回调策略上下文对象的方法。
      实例代码:
    package com.java.dp.strategy;
    //抽象策略类
    public interface TravelStrategy {
        void travelType();
    }
    
    
    package com.java.dp.strategy;
    
    public class TrainStrategy implements TravelStrategy{
        @Override
        public void travelType() {
            System.out.println("乘坐火车出行");
        }
    }
    
    
    package com.java.dp.strategy;
    
    public class CarStrategy implements TravelStrategy{
        @Override
        public void travelType() {
            System.out.println("开车出行");
        }
    }
    
    
    package com.java.dp.strategy;
    
    public class AirStrategy implements TravelStrategy{
        @Override
        public void travelType() {
            System.out.println("乘坐飞机出行");
        }
    }
    
    
    package com.java.dp.strategy;
    //策略上下文角色
    public class Traveler {
        //出行策略
        private TravelStrategy travelStrategy;
    
        //设置出行策略
        public Traveler(TravelStrategy travelStrategy){
            this.travelStrategy = travelStrategy;
        }
        public void travelStyle(){
            this.travelStrategy.travelType();
        }
    
    
    }
    
    
    package com.java.dp.strategy;
    
    import java.util.Random;
    
    public class Clent {
        public static void main(String[] args) {
            TravelStrategy travelStrategy = null;
            for(int i = 0;i<3;i++){
                System.out.println("要去杭州玩了,可以由以下的选择方式:");
                System.out.println("1飞机");
                System.out.println("2火车");
                System.out.println("3汽车");
                System.out.println("随机选择");
                int num = new Random().nextInt(3)+1;
                if(num==1){
                    travelStrategy = new AirStrategy();
                }else if (num == 2){
                    travelStrategy = new TrainStrategy();
                }else {
                    travelStrategy = new CarStrategy();
                }
                Traveler li = new Traveler(travelStrategy);
                li.travelStyle();
            }
        }
    }
    
    

    优点:

    • 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。
    • 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而 避免重复的代码。
    • 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
    • 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
    • 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

    缺点:

    • 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
    • 策略模式造成很多的策略类。

    应用:

    • 多个类只有在算法或行为上稍有不同的场景
    • 算法需要自由切换的场景
    • 需要屏蔽算法规则的场景

    观察者(Observer)模式

    定义:
    指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
    结构:

    1. 被观察者(Subject):也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
    2. 具体的被观察者(Concrete Subject):也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
    3. 观察者(Observer):它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
      实例代码:
    package com.java.dp.Observer;
    
    //被观察者
    public interface Observable {
    
        //添加观察者
        void addObserver(Observer observer);
        //删除观察者
        void deleteObserver(Observer observer);
        //被观察者有所行动,通知观察者
        void notifyObserver(String context);
    }
    
    
    package com.java.dp.Observer;
    //观察者接口
    public interface Observer {
        //一旦发现被观察者有动静,自己有所行动
        void update(String context);
    }
    
    
    package com.java.dp.Observer;
    
    public interface IStar {
        void eat();
        void action();
    }
    
    
    package com.java.dp.Observer;
    
    public class Fans implements Observer{
        @Override
        public void update(String context) {
            System.out.println("粉丝:观察到明星活动");
            this.action(context);
        }
        private void action(String context){
            System.out.println(context+"-->斯国一!");
        }
    }
    
    
    package com.java.dp.Observer;
    
    public class Reporter implements Observer{
        @Override
        public void update(String context) {
            System.out.println("记者:观察到明星活动");
            this.action(context);
        }
        private void action(String context){
            System.out.println(context+"-->演技高超之!");
        }
    }
    
    
    package com.java.dp.Observer;
    
    import java.util.ArrayList;
    
    public class Wujing implements IStar,Observable{
        private ArrayList<Observer> oblist = new ArrayList<>();
        @Override
        public void eat() {
            System.out.println("超级战狼进食ing");
            this.notifyObserver("超级战狼进食ing");
        }
    
        @Override
        public void action() {
            System.out.println("超级战狼拍片ing");
            this.notifyObserver("超级战狼拍片ing");
        }
    
        @Override
        public void addObserver(Observer observer) {
            this.oblist.add(observer);
        }
    
        @Override
        public void deleteObserver(Observer observer) {
            this.oblist.remove(observer);
        }
    
        @Override
        public void notifyObserver(String context) {
            for (Observer observer:oblist) {
                //todo
                observer.update(context);
            }
        }
    }
    
    
    package com.java.dp.Observer;
    
    public class Client {
        public static void main(String[] args) {
            Observer fan = new Fans();
            Observer reporter = new Reporter();
            Wujing wu = new Wujing();
    
            wu.addObserver(fan);
            wu.addObserver(reporter);
    
            wu.eat();
            wu.action();
        }
    
    
    
    }
    
    

    在这里插入图片描述

    优点:

    1. 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
    2. 目标与观察者之间建立了一套触发机制。

    缺点:

    • 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
    • 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

    应用:

    1. 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
    2. 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可 以各自独立地改变和复用。

    迭代器(Iterator)模式

    定义:
    提供一种方法访问一个容器对象中各个元素,而又不需要暴露该对象的内部细节表示。 迭代器是为容器服务的,什么是容器?能盛放对象的所有类型都可以认为是容器。例如我们学过的Collection集合 类型等。
    结构:

    1. 抽象容器(Aggregate)角色:定义存储、添加、删除容器对象以及创建迭代器对象的接口。
    2. 具体容器(ConcreteAggregate)角色:实现抽象容器类,返回一个具体迭代器的实例。
    3. 抽象迭代器(Iterator)角色:定义访问和遍历容器元素的接口,通常包含 hasNext()、first()、next() 等方法。
    4. 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对容器对象的遍历,记录遍历的当前位置。
      实例代码:
    package com.java.dp.iterator;
    
    public interface IProject {
        //查看信息
        String getProjectInfo();
        //添加项目
        void add(IProject pro);
        //获得一个遍历对象
        IprojectIterator iterator();
    }
    
    
    package com.java.dp.iterator;
    
    
    import java.util.Iterator;
    
    public interface IprojectIterator extends Iterator {
    }
    
    
    package com.java.dp.iterator;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Project implements IProject{
        //存放
        private List<IProject> list = new ArrayList<>();
        private String name;
        private int num;
        private int cost;
        public Project(){}
        public Project(String name,int num,int cost){
            this.name = name;
            this.num = num;
            this.cost = cost;
        }
        @Override
        public String getProjectInfo() {
            String info = "项目名称:"+name+"项目人数:"+num+"项目花费:"+cost;
            return info;
        }
    
        @Override
        public void add(IProject pro) {
            this.list.add(pro);
        }
    
        @Override
        public IprojectIterator iterator() {
            //todo 返回迭代器对象
            return new ProjectIterator(list);
        }
    }
    
    
    package com.java.dp.iterator;
    
    import java.util.List;
    
    public class ProjectIterator implements IprojectIterator{
        //存放所有项目的集合
        private List<IProject> list;
        private int cur;
        public ProjectIterator(List<IProject> list){
            this.list = list;
        }
        //判断是否还有需要遍历的元素
        @Override
        public boolean hasNext() {
            boolean flag = true;
            if(this.cur>=list.size()||this.list.get(this.cur)==null){
                flag = false;
            }
            return flag;
        }
    
        //获取下一个元素值
        @Override
        public Object next() {
            return this.list.get(this.cur++);
        }
    
    
    }
    
    
    package com.java.dp.iterator;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Client {
        public static void main(String[] args) {
            IProject project = new Project();
            project.add(new Project("新功夫",213,123));
            project.add(new Project("新开始",2113,1123));
            project.add(new Project("新颇说",21113,12113));
            IprojectIterator iterator = project.iterator();
            while (iterator.hasNext()){
                IProject pro = (IProject) iterator.next();
                System.out.println(pro.getProjectInfo());
            }
        }
    }
    
    

    在这里插入图片描述

    优点:

    1. 访问一个聚合对象的内容而无须暴露它的内部表示。
    2. 遍历任务交由迭代器完成,这简化了聚合类。
    3. 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
    4. 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
    5. 封装性良好,为遍历不同的聚合结构提供一个统一的接口。
      缺点:
      增加了类的个数,这在一定程度上增加了系统的复杂性。

    模板方法(Template Method)模式

    定义:
    定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义 该算法的某些特定步骤。它是应用非常广泛的模式,使用了java的继承机制。
    实例代码:

    package com.java.dp.TemplateMethod;
    
    public abstract class HummerModel {
        public abstract void start();
        public abstract void stop();
        public abstract void alarm();
        public abstract void engineBoom();
        public void run(){
            this.start();
            this.engineBoom();
            if(this.isAlarm()){
                this.alarm();
            }
            this.stop();
        };
        //钩子方法
        protected boolean isAlarm(){
            return true;
        }
    }
    
    
    package com.java.dp.TemplateMethod;
    
    public class HummerModel1 extends HummerModel{
        private  boolean alarmFlag = true;
    
        @Override
        protected boolean isAlarm() {
            return alarmFlag;
        }
    
        public void isAlarmFlag(boolean alarmFlag) {
            this.alarmFlag = alarmFlag;
        }
    
        @Override
        public void start() {
            System.out.println("悍马H1启动");
        }
    
        @Override
        public void stop() {
            System.out.println("悍马H1停止");
        }
    
        @Override
        public void alarm() {
            System.out.println("悍马H1鸣笛");
        }
    
        @Override
        public void engineBoom() {
            System.out.println("悍马H1引擎轰鸣");
        }
    
    
    }
    
    
    package com.java.dp.TemplateMethod;
    
    public class HummerModel2 extends HummerModel{
        @Override
        public void start() {
            System.out.println("悍马H2启动");
        }
    
        @Override
        public void stop() {
            System.out.println("悍马H2停止");
        }
    
        @Override
        public void alarm() {
            System.out.println("悍马H2鸣笛");
        }
    
        @Override
        public void engineBoom() {
            System.out.println("悍马H2引擎轰鸣");
        }
    
        @Override
        protected boolean isAlarm() {
            return false;
        }
    }
    
    
    package com.java.dp.TemplateMethod;
    
    import java.util.Scanner;
    
    public class Client {
        public static void main(String[] args) {
            System.out.println("H1是否需要鸣笛?0--需要 1--不需要");
            Scanner input = new Scanner(System.in);
            String type = input.nextLine();
    
            HummerModel1 h1 = new HummerModel1();
            if("1".equals(type)){
                h1.isAlarmFlag(false);
            }
            h1.run();
            HummerModel2 h2 = new HummerModel2();
            h2.run();
        }
    }
    
    

    在这里插入图片描述
    在这里插入图片描述

    优点:

    1. 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子 类继承实现,便于子类继续扩展。
    2. 它在父类中提取了公共的部分代码,便于代码复用。
    3. 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

    缺点:

    1. 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
    2. 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了 代码阅读的难度。

    应用:
    1、多个子类有共有的方法,并且基本逻辑相同的时候

    2、重要复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能可以由各个子类实现

    3、重构的时候,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子方法约束其行 为。

    展开全文
  • Java设计模式-代理模式之动态代理(附源代码分析) 动态代理概念及类图 上一篇中介绍了静态代理,动态代理跟静态代理一个最大的差别就是:动态代理是在执行时刻动态的创建出代理类及其对象。...

    Java设计模式-代理模式之动态代理(附源代码分析)

    动态代理概念及类图

    上一篇中介绍了静态代理,动态代理跟静态代理一个最大的差别就是:动态代理是在执行时刻动态的创建出代理类及其对象。

    上篇中的静态代理是在编译的时候就确定了代理类详细类型。假设有多个类须要代理。那么就得创建多个。

    另一点,假设Subject中新增了一个方法,那么相应的实现接口的类中也要相应的实现这些方法。

    动态代理的做法:在执行时刻。能够动态创建出一个实现了多个接口的代理类。每一个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接 口的实现。当使用者调用了代理对象所代理的接口中的方法的时候。这个调用的信息会被传递给InvocationHandler的invoke方法。在 invoke方法的參数中能够获取到代理对象、方法相应的Method对象和调用的实际參数。invoke方法的返回值被返回给使用者。这样的做法实际上相 当于对方法调用进行了拦截。

    类图例如以下所看到的:

    上面类图中使用的JDK中的Proxy类。所以是须要要办法来告诉Proxy类须要做什么,不能像静态代理一样。将代码放到Proxy类中,由于如今Proxy不是直接实现的。既然这种代码不能放在Proxy类中,那么就须要一个InvocationHandler,InvocationHandler的工作就是响应代理的不论什么调用。


    动态代理实现过程

    详细有例如以下四步骤:

    通过实现 InvocationHandler 接口创建自己的调用处理器;通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;通过反射机制获得动态代理类的构造函数,其唯一參数类型是调用处理器接口类型。通过构造函数创建动态代理类实例,构造时调用处理器对象作为參数被传入。

    一个详细的样例

    接着上面的类图和静态代理中的样例,我们分别创建Subject和RealSubject

    Subject

    package ProxyMode;
    
    /*
     * 抽象接口。相应类图中的Subject
     * 
     */
    
    public interface Subject {
    
        public void SujectShow();
    
    }
    
    
    

    RealSubject

    package ProxyMode;
    
    
    public class RealSubject implements Subject{
    
        @Override
        public void SujectShow() {
            // TODO Auto-generated method stub
            System.out.println("杀人是我指使的,我是幕后黑手!

    By---"+getClass()); } }

    建立InvocationHandler用来响应代理的不论什么调用

    package ProxyMode;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class ProxyHandler implements InvocationHandler {
    
        private Object proxied;   
    
          public ProxyHandler( Object proxied )   
          {   
            this.proxied = proxied;   
          }   
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
    
            System.out.println("准备工作之前:");
    
            //转调详细目标对象的方法
              Object object=   method.invoke( proxied, args);
    
             System.out.println("工作已经做完了!");
             return object;
        }
    
    }
    
    

    动态代理类測试,这个代理类中再也不用实现Subject接口。能够动态的获得RealSubject接口中的方法

    package ProxyMode;
    
    
    import java.lang.reflect.Proxy;
    
    public class DynamicProxy  {
    
        public static void main( String args[] )   
          {   
            RealSubject real = new RealSubject();   
            Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(), 
             new Class[]{Subject.class}, 
             new ProxyHandler(real));
    
            proxySubject.SujectShow();;
    
          }   
    }
    

    測试结果

    准备工作之前:
    杀人是我指使的,我是幕后黑手!

    By---class ProxyMode.RealSubject 工作已经做完了!

    Proxy和InvocationHandler重要部分源代码分析

    java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。

    清单 1. Proxy 的静态方法
    // 方法 1: 该方法用于获取指定代理对象所关联的调用处理器,比方上面代码中的ProxyHandler
    static InvocationHandler getInvocationHandler(Object proxy) 
    
    // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
    static Class getProxyClass(ClassLoader loader, Class[] interfaces) 
    
    // 方法 3:该方法用于推断指定类对象是否是一个动态代理类
    static boolean isProxyClass(Class cl) 
    
    // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
    static Object newProxyInstance(ClassLoader loader, Class[] interfaces, 
        InvocationHandler h)
    

    以下重点看看newPRoxyInstance方法:

    
       public static Object newProxyInstance(ClassLoader loader, 
                Class<?

    >[] interfaces, InvocationHandler h) throws IllegalArgumentException { // 检查 h 不为 空,否则抛异常 if (h == null) { throw new NullPointerException(); } // 获得与制定类装载器和一组接口相关的代理类类型对象 Class cl = getProxyClass(loader, interfaces); // 通过反射获取构造函数对象并生成代理类实例 try { Constructor cons = cl.getConstructor(constructorParams); return (Object) cons.newInstance(new Object[] { h }); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); } }

    看这种方法的三个參数

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
    

    loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行载入interfaces: 一个Interface对象的数组。表示的是我将要给我须要代理的对象提供一组什么接口。假设我提供了一组接口给它。那么这个代理对象就宣称实现了该接口(多态)。这样我就能调用这组接口中的方法了

    h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

    从上面JDK源代码中能够看出getProxyClass方法才是newProxyInstance方法中最重要的,该方法负责为一组接口动态地生成代理类类型对象。以下開始解析proxy中的getProxyClass方法

    该方法总共能够分为四个步骤:

    对这组接口进行一定程度的安全检查,包含检查接口类对象是否对类装载器可见而且与类装载器所能识别的接口类对象是全然同样的,还会检查确保是 interface 类型而不是 class 类型。

    这个步骤通过一个循环来完毕,检查通过后将会得到一个包括全部接口名称的字符串数组,记为 String[] interfaceNames

     for (int i = 0; i < interfaces.length; i++) {
    
                // 验证类载入程 序 解 析 该接口到同一类对象的名称。

    String interfaceName = interfaces[i].getName(); Class<?> interfaceClass = null; try { interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " is not visible from class loader"); } // 验证类对象真正代表一个接口 if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } //验证这个接口是不是反复的 if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); //interfaceset是一个hashset集合 interfaceNames[i] = interfaceName; }

    从 loaderToCache 映射表中获取以类装载器对象为keyword所相应的缓存表。假设不存在就创建一个新的缓存表并更新到 loaderToCache。

    缓存表是一个 HashMap 实例,正常情况下它将存放键值对(接口名字列表。动态生成的代理类的类对象引用)。当代理类正在被创建时它会暂时保存(接口名字列表,pendingGenerationMarker)。标记 pendingGenerationMarke 的作用是通知兴许的同类请求(接口数组同样且组内接口排列顺序也同样)代理类正在被创建。请保持等待直至创建完毕。

     synchronized (cache) {
    do { 
        // 以接口名字列表作为关键字获得相应 cache 值
        Object value = cache.get(key); 
        if (value instanceof Reference) { 
            proxyClass = (Class) ((Reference) value).get(); 
        } 
        if (proxyClass != null) { 
            // 假设已经创建,直接返回,这里很重要。假设已经创建过代理类,那么不再创建
            return proxyClass; 
        } else if (value == pendingGenerationMarker) { 
            // 代理类正在被创建,保持等待
            try { 
                cache.wait(); 
            } catch (InterruptedException e) { 
            } 
            // 等待被唤醒,继续循环并通过二次检查以确保创建完毕,否则又一次等待
            continue; 
        } else { 
            // 标记代理类正在被创建
            cache.put(key, pendingGenerationMarker); 
            // break 跳出循环已进入创建过程
            break; 
    } while (true);
    }
    

    动态创建代理类的类对象。

    首先是确定代理类所在的包,其原则如前所述。假设都为 public 接口,则包名为空字符串表示顶层包。假设全部非 public 接口都在同一个包。则包名与这些接口的包名相同;假设有多个非 public 接口且不同包,则抛异常终止代理类的生成。

    确定了包后。就開始生成代理类的类名,相同如前所述按格式“$ProxyN”生成。

    // 动态地生成代 理类的字节码数组
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); 
    try { 
        // 动态地定义新生成的代理类
        proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, 
            proxyClassFile.length); 
    } catch (ClassFormatError e) { 
        throw new IllegalArgumentException(e.toString()); 
    } 
    
    // 把生成的代理类的类对象记录进 proxyClasses 表
    proxyClasses.put(proxyClass, null);
    
    

    到了这里,事实上generateProxyClass方法也是一个重点,可是generateProxyClass的方法代码跟踪不了。位于并未公开的 sun.misc 包,有若干常量、变量和方法以完毕这个奇妙的代码生成的过程,可是 sun 并没有提供源码以供研读

    结尾部分
    依据结果更新缓存表,假设成功则将代理类的类对象引用更新进缓存表。否则清楚缓存表中相应关键值,最后唤醒全部可能的正在等待的线程。

    
     synchronized (cache) {
                    if (proxyClass != null) {
                        cache.put(key, new WeakReference<Class<?>>(proxyClass));
                    } else {
                        cache.remove(key);
                    }
                    cache.notifyAll();
                }
    

    java.lang.reflect.InvocationHandler:这是调用处理器接口,它自己定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对托付类的代理訪问。

    InvocationHandler 的核心方法。我们最关心的是Invoke方法为什么会被调用。见以下分析:

    // 该方法负责集中处理动态代理类上的所 有方法调用。

    //第一个參数既是代理类实例, //第二个參数是被调用的方法对象 // 第三个方法是调用參数。调用处理器依据这三个參数进行预处理或分派到托付类实例上发射运行 Object invoke(Object proxy, Method method, Object[] args)

    每次生成动态代理类对象时都须要指定一个实现了该接口的调用处理器对象(參见 newProxyInstance 的第三个參数)。

    非常多人肯定跟我一样,我们在Handler中调用的method.invoke方法中并没有显示的调用invoke方法,仅仅是在newProxyInstance中应用了一个handler对象。有了上面关于newProxyInstance的源代码分析。我们知道了 newproxyinstance生成了一个$Proxy0类代理。当调用Subjectshow()方法时,事实上调用的$Proxy0的SubjectShow()方法,从而调用父类Proxy中传进来第三个參数(h)的的Invoke方法。

    //这种方法是 Proxy源代码中的
      protected Proxy(InvocationHandler h) {
            this.h = h;
        }
    
    

    来看NewProxyInstance方法生成的$Proxy0代理类的源代码

    public final class $Proxy0 extends Proxy implements Subject {
        private static Method m1;
        private static Method m0;
        private static Method m3;
        private static Method m2;
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals",
                        new Class[] { Class.forName("java.lang.Object") });
    
                m0 = Class.forName("java.lang.Object").getMethod("hashCode",
                        new Class[0]);
    
                m3 = Class.forName("***.RealSubject").getMethod("request",
                        new Class[0]);
    
                m2 = Class.forName("java.lang.Object").getMethod("toString",
                        new Class[0]);
    
            } catch (NoSuchMethodException nosuchmethodexception) {
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());
            } catch (ClassNotFoundException classnotfoundexception) {
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());
            }
        } //static
    
        public $Proxy0(InvocationHandler invocationhandler) {
            super(invocationhandler);
        }
    
        @Override
        public final boolean equals(Object obj) {
            try {
                return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
            } catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        @Override
        public final int hashCode() {
            try {
                return ((Integer) super.h.invoke(this, m0, null)).intValue();
            } catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        public final void SubjectShow() {
            try {
                super.h.invoke(this, m3, null); //就是这个地方  调用h.invoke()
                return;
            } catch (Error e) {
            } catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    
        @Override
        public final String toString() {
            try {
                return (String) super.h.invoke(this, m2, null);
            } catch (Throwable throwable) {
                throw new UndeclaredThrowableException(throwable);
            }
        }
    }
    
    

    从上面的$Proxy0中找到方法SubjectSHow()方法,我们能够看到中间调用了父类Proxy的參数Handler h的invoke方法,也就调用了ProxyHandler中的invoke()方法。还能够看到¥Proxy0还代理了equals()、hashcode()、tostring()这三个方法,至此动态代理实现机制就非常清楚了

    參考文章:http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/
    展开全文
  • 这是我见过的最好的设计模式讲解资料。为什么这么说呢?因为它不像其他的书籍那样,只讲解原理,还把每个设计模式的由来动机也讲解得很详细。它的讲解思路为:模式动机->模式定义->模式结构->模式分析(即原理)->...
  • 适配器模式(Adapter Pattern)是指将一个类的接口转换成客户期望的另一个接口,使 原本的接口不兼容的类可以一起工作,属于结构型设计模式。 适配器模式的应用场景 1、已经存在的类,它的方法和需求不匹配(方法...


    描述


    博文简介

    通过学习适配模式,学会优雅地解决代码功能的兼容问题。

    适配器模式的定义及应用场景

    适配器模式的定义

    适配器模式(Adapter Pattern)是指将一个类的接口转换成客户期望的另一个接口,使 原本的接口不兼容的类可以一起工作,属于结构型设计模式。

    适配器模式的应用场景

    1、已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况。
    2、适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。

    适配器模式实际使用案例分析

    生活中也非常的应用场景,例如电源插转换头、手机充电转换头、显示器转接头等等。

    简单场景实现

    在咱们国家民用电是220V交流电,但我们手机使用的锂电池使用的5V直流电。因此,我们给手机充电时就需要使用电源适配器(充电器)来进行转换。下面我们有代码来还原这个生活中的场景。
    创建AC220类,表示 220V交流电:

    public class AC220 { 
    	public int outputAC220V(){ 
    		int output = 220; 
    		System.out.println("交流电"+output+"V"); 
    		return output; 
    	} 
    }
    

    创建DC5接口,表示5V直流电:

    public interface DC5 { 
    	int outputDC5V(); 
    }
    

    创建电源适配器PowerAdapter类:

    public class PowerAdapter implements DC5{ 
    	private AC220 ac220; 
    	public PowerAdapter(AC220 ac220){ 
    		this.ac220 = ac220; 
    	}
    	public int outputDC5V() { 
    		int adapterInput = ac220.outputAC220V(); 
    		//变压操作
    		int adapterOutput = adapterInput/44; 
    		System.out.println("PowerAdapter输入AC:"+adapterInput+"V"+"输出 DC:"+adapterOutput+"V"); 
    		return adapterOutput; 
    	} 
    }
    

    客户端测试代码:

    public class ObjectAdapterTest { 
    	public static void main(String[] args) { 
    		DC5 dc5 = new PowerAdapter(new AC220()); 
    		dc5.outputDC5V(); 
    	} 
    }
    

    通过这样一个案例,通过增加 PowerAdapter 电源适配器,实现了二者的兼容。太小儿科是吧,不好忽悠的,那么下面来结合一个常见的业务场景给大家深入分析一番。

    利用适配器模式重构第三登录自由适配的业务场景

    我们来一个实际的业务场景,利用适配模式来解决实际问题。有一定开发经历过的同学都会遇到这样的问题。很早以前开发的老系统都有登录接口,但是随着业务的发展和社会的进步,单纯地依赖用户名密码登录显然不能满足用户需求了。现在,我们大部分系统都已经支持多种登录方式,如 QQ 登录、微信登录、手机登录、微 博登录等等,同时保留用户名密码的登录方式。虽然登录形式丰富了,但是登录后的处理逻辑可以不必改,同样是将登录状态保存到 session,遵循开闭原则。

    首先创建统一的 返回结果 ResultMsg 类:

    public class ResultMsg { 
    	private int code; 
    	private String msg; 
    	private Object data; 
    	
    	public ResultMsg(int code, String msg, Object data) { 
    		this.code = code; 
    		this.msg = msg; 
    		this.data = data; 
    	}
    	
    	public int getCode() { 
    		return code; 
    	}
    	public void setCode(int code) { 
    		this.code = code; 
    	}
    	public String getMsg() { 
    		return msg; 
    	}
    	public void setMsg(String msg) { 
    		this.msg = msg; 
    	}
    	public Object getData() { 
    		return data; 
    	}
    	public void setData(Object data) { 
    		this.data = data; 
    	}
    }
    

    假设老系统的登录逻辑 SiginService:

    public class SiginService { 
    	/*** 注册方法 */ 
    	public ResultMsg regist(String username,String password){ 
    		return new ResultMsg(200,"注册成功",new Member()); 
    	}
    	/*** 登录的方法*/ 
    	public ResultMsg login(String username,String password){ 
    		return null; 
    	} 
    }
    

    为了遵循开闭原则,老系统的代码我们不会去修改。那么下面开始本次代码重构之路。
    先创建 Member 类:

    public class Member {
    		private String username; 
    		private String password; 
    		private String mid; 
    		private String info; 
    	
    	public String getUsername() { 
    		return username; 
    	}
    	public void setUsername(String username) { 
    		this.username = username; 
    	}
    	public String getPassword() { 
    		return password; 
    	}
    	public void setPassword(String password) { 
    		this.password = password; 
    	}
    	public String getMid() { 
    		return mid; 
    	}
    	public void setMid(String mid) { 
    		this.mid = mid; 
    	}
    	public String getInfo() { 
    		return info; 
    	}
    	public void setInfo(String info) { 
    		this.info = info; 
    	} 
    }
    

    创建一个新的类SigninForThirdService继承原来的逻辑,以前的代码我们不去改动:

    public class SigninForThirdService extends SiginService { 
    	public ResultMsg loginForQQ(String openId){ 
    		//1、openId 是全局唯一,我们可以把它当做是一个用户名(加长) 
    		//2、密码默认为 QQ_EMPTY 
    		//3、注册(在原有系统里面创建一个用户) 
    		//4、调用原来的登录方法 
    		return loginForRegist(openId,null); 
    	}
    	public ResultMsg loginForWechat(String openId){ 
    		return null; 
    	}
    	public ResultMsg loginForToken(String token){ 
    		//通过 token 拿到用户信息,然后再重新登陆了一次 
    		return null; 
    	}
    	public ResultMsg loginForTelphone(String telphone,String code){ 
    		return null; 
    	}
    	public ResultMsg loginForRegist(String username,String password){
    		super.regist(username,null); 
    		return super.login(username,null); 
    	} 
    }
    

    测试代码:

    public class SigninForThirdServiceTest { 
    	public static void main(String[] args) { 
    		SigninForThirdService service = new SigninForThirdService(); 
    		//不改变原来的代码,也要能够兼容新的需求,还可以再加一层策略模式 
    		service.loginForQQ("sdfgdgfwresdf9123sdf"); 
    	} 
    }
    

    通过这么一个简单的适配,已经完成了代码兼容。当然,我们的代码还不够优雅,可以进一步根据不同的登录方式,创建不同的Adapter。

    更加优雅的解决方式

    创建 LoginAdapter 接口:

    public interface LoginAdapter { 
    	boolean support(Object adapter); 
    	ResultMsg login(String id,Object adapter); 
    }
    

    QQ登录LoginForQQAdapter:

    public class LoginForQQAdapter implements LoginAdapter { 
    	public boolean support(Object adapter) { 
    		return adapter instanceof LoginForQQAdapter; 
    	}
    	public ResultMsg login(String id, Object adapter) {
    		return null; 
    	} 
    }
    

    新浪微博登录LoginForSinaAdapter:

    public class LoginForSinaAdapter implements LoginAdapter { 
    	public boolean support(Object adapter) { 
    		return adapter instanceof LoginForSinaAdapter; 
    	}
    	public ResultMsg login(String id, Object adapter) { 
    		return null; 
    	} 
    }
    

    手机号登录LoginForTelAdapter:

    public class LoginForTelAdapter implements LoginAdapter { 
    	public boolean support(Object adapter) { 
    		return adapter instanceof LoginForTelAdapter; 
    	}
    	public ResultMsg login(String id, Object adapter) { 
    		return null; 
    	} 
    }
    

    Token自动登录LoginForTokenAdapter:

    public class LoginForTokenAdapter implements LoginAdapter { 
    	public boolean support(Object adapter) { 
    		return adapter instanceof LoginForTokenAdapter; 
    	}
    	public ResultMsg login(String id, Object adapter) { 
    		return null; 
    	} 
    }
    

    微信登录LoginForWechatAdapter:

    public class LoginForWechatAdapter implements LoginAdapter { 
    	public boolean support(Object adapter) { 
    		return adapter instanceof LoginForWechatAdapter; 
    	}
    	public ResultMsg login(String id, Object adapter) { 
    		return null; 
    	} 
    }
    

    创建第三方登录兼容接口IPassportForThird:

    public interface IPassportForThird { 
    	/*** QQ 登录 */ 
    	ResultMsg loginForQQ(String id); 
    	/*** 微信登录 */ 
    	ResultMsg loginForWechat(String id); 
    	/*** 记住登录状态后自动登录 */ 
    	ResultMsg loginForToken(String token); 
    	/*** 手机号登录 */ 
    	ResultMsg loginForTelphone(String telphone, String code); 
    	/*** 注册后自动登录 */ 
    	ResultMsg loginForRegist(String username, String passport); 
    }
    

    实现兼容 PassportForThirdAdapter:

    /*** 第三方登录自由适配 */ 
    public class PassportForThirdAdapter extends SiginService implements IPassportForThird { 
    	public ResultMsg loginForQQ(String id) { 
    		return processLogin(id,LoginForQQAdapter.class); 
    	}
    	public ResultMsg loginForWechat(String id) { 
    		return processLogin(id,LoginForWechatAdapter.class); 
    	}
    	public ResultMsg loginForToken(String token) { 
    		return processLogin(token,LoginForTokenAdapter.class); 
    	}
    	public ResultMsg loginForTelphone(String telphone, String code) { 
    		return processLogin(telphone,LoginForTelAdapter.class); 
    	}
    	public ResultMsg loginForRegist(String username, String passport) { 
    		super.regist(username,null); 
    		return super.login(username,null); 
    	}
    	//这里用到了简单工厂模式及策略模式 
    	private ResultMsg processLogin(String key,Class<? extends LoginAdapter> clazz){ 
    		try {
    			LoginAdapter adapter = clazz.newInstance(); 
    			if(adapter.support(adapter)) { 
    			return adapter.login(key, adapter); 
    			}else { 
    			return null; 
    			} 
    		}catch (Exception e){ 
    			e.printStackTrace(); 
    		}
    	return null; 
    	} 
    }
    

    测试代码:

    public class PassportTest { 
    	public static void main(String[] args) { 
    		IPassportForThird passportForThird = new PassportForThirdAdapter(); 
    		passportForThird.loginForQQ(""); 
    	} 
    }
    

    至此,我们在遵循开闭原则的前提下,完整地实现了一个兼容多平台登录的业务场景。 当然,我目前的这个设计也并不完美,仅供参考。

    适配器模式在源码中的体现

    Spring中适配器模式应用得也非常广泛,例如:SpringAOP中的AdvisorAdapter类, 它有三个实现类 MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter和ThrowsAdviceAdapter。
    先来看顶层接口 AdvisorAdapter 的源代码:

    package org.springframework.aop.framework.adapter; 
    import org.aopalliance.aop.Advice; 
    import org.aopalliance.intercept.MethodInterceptor; 
    import org.springframework.aop.Advisor; 
    
    public interface AdvisorAdapter { 
    	boolean supportsAdvice(Advice var1); 
    	MethodInterceptor getInterceptor(Advisor var1); 
    }
    

    再看 MethodBeforeAdviceAdapter 类:

    package org.springframework.aop.framework.adapter; 
    import java.io.Serializable; 
    import org.aopalliance.aop.Advice; 
    import org.aopalliance.intercept.MethodInterceptor; 
    import org.springframework.aop.Advisor; 
    import org.springframework.aop.MethodBeforeAdvice; 
    
    	class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable { 
    	MethodBeforeAdviceAdapter() { }
    	public boolean supportsAdvice(Advice advice) { 
    		return advice instanceof MethodBeforeAdvice; 
    	}
    	public MethodInterceptor getInterceptor(Advisor advisor) { 
    		MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice(); 
    		return new MethodBeforeAdviceInterceptor(advice); 
    	} 
    }
    

    其它两个类这里就不把代码贴出来了。Spring 会根据不同的 AOP 配置来确定使用对应的Advice,跟策略模式不同的一个方法可以同时拥有多个 Advice。

    再来看一个 SpringMVC 中的 HandlerAdapter 类,它也有多个子类,其适配调用的关键代码还是在 DispatcherServlet 的 doDispatch()方法中,
    下面我们还 是来看源码:

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 
    	HttpServletRequest processedRequest = request; 
    	HandlerExecutionChain mappedHandler = null; 
    	boolean multipartRequestParsed = false; 
    	WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 
    	try {
    		try {
    		ModelAndView mv = null; 
    		Object dispatchException = null; 
    		try {
    			processedRequest = this.checkMultipart(request); 
    			multipartRequestParsed = processedRequest != request; 
    			mappedHandler = this.getHandler(processedRequest); 
    			if(mappedHandler == null) { 
    				this.noHandlerFound(processedRequest, response); 
    				return; 
    		}
    			HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); 
    			String method = request.getMethod(); 
    			boolean isGet = "GET".equals(method); 
    			if(isGet || "HEAD".equals(method)) { 
    			long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); 
    			if(this.logger.isDebugEnabled()) { 
    			this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
    		}
    		if((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) { 
    				return; 
    			} 
    		}
    		if(!mappedHandler.applyPreHandle(processedRequest, response)) { 
    			return; 
    		}
    			mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 
    			if(asyncManager.isConcurrentHandlingStarted()) { 
    			return; 
    		}
    		this.applyDefaultViewName(processedRequest, mv); 
    			mappedHandler.applyPostHandle(processedRequest, response, mv); 
    		} catch (Exception var20) { 
    			dispatchException = var20; 
    		} catch (Throwable var21) { 
    			dispatchException = new NestedServletException("Handler dispatch failed", var21); 
    		}
    			this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); 
    		} catch (Exception var22) { 
    			this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22); 
    		} catch (Throwable var23) { 
    			this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23)); 
    		} 
    		} finally { 
    			if(asyncManager.isConcurrentHandlingStarted()) { 
    			if(mappedHandler != null) { 
    				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); 
    			} 
    		} else if(multipartRequestParsed) { 
    			this.cleanupMultipart(processedRequest); 
    		} 
    	} 
    }
    

    在 doDispatch()方法中调用了getHandlerAdapter()方法,来看代码:

    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { 
    	if(this.handlerAdapters != null) { 
    		Iterator var2 = this.handlerAdapters.iterator(); 
    		while(var2.hasNext()) { 
    			HandlerAdapter ha = (HandlerAdapter)var2.next(); 
    			if(this.logger.isTraceEnabled()) { 
    				this.logger.trace("Testing handler adapter [" + ha + "]"); 
    			}
    			if(ha.supports(handler)) { 
    				return ha; 
    			}
    		} 
    	}
    	throw new ServletException("No adapter for handler [" + handler + "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); 
    }
    

    在getHandlerAdapter()方法中循环调用了supports()方法判断是否兼容,循环迭代集 合中的 Adapter 又是在初始化时早已赋值。这里不再深入,感兴趣的同学可以自己研究或者继续关注我后续的源码专题的博客。

    适配器模式的优缺点

    优点
    1、能提高类的透明性和复用,现有的类复用但不需要改变。
    2、目标类和适配器类解耦,提高程序的扩展性。
    3、在很多业务场景中符合开闭原则。
    缺点
    1、适配器编写过程需要全面考虑,可能会增加系统的复杂性。
    2、增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。

    题外话

    学习到这里,相信小伙伴会有一个疑问了:适配器模式跟策略模式好像区别不大,在这里强调一下,适配器模式主要解决的是功能兼容问题,单场景适配大家可能不会和策略模式有对比。但多场景适配大家产生联想和混淆了。其实,大家有没有发现一个细 节,我给每个适配器都加上了一个 support()方法,用来判断是否兼容,support()方法 的参数也是 Object 的,而 supoort()来自于接口。适配器的实现逻辑并不依赖于接口, 我们完全可以将 LoginAdapter 接口去掉。而加上接口,只是为了代码规范。

    展开全文
  • 1)单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能2)当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用...
  • 委派模式不属于GOF23种设计模式中。委派模式(Delegate Pattern)的基本作用就是负责任务的调用和分配任务,跟代理模式很像,可以看做是一种特殊情况下的静态代理的全权代理,但是代理模式注重过程,而委派模式注重...
  • Java设计模式-策略模式分析

    千次阅读 2020-10-05 21:52:16
    通过对策略模式的学习,消除程序中大量的冗余代码和多重条件转移语句。 策略模式的定义及应用场景 策略模式的定义 策略模式(Strategy Pattern)是指定义了算法家族、分别封装起来,让它们之间可以互 相替换,此模式...
  • 文章目录Java 设计模式内容介绍先看几个经典的面试题设计模式的重要性设计模式七大原则设计模式的目的设计模式七大原则单一职责原则基本介绍应用实例单一职责原则注意事项和细节接口隔离原则基本介绍应用实例应传统...
  • 前言原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。...
  • 本文实例讲述了Java责任链设计模式。分享给大家供大家参考,具体如下:一 代码abstract class AbstractHandler {private Handler Handler = null;// 持有责任链中下一个责任处理者的引用public void setHandler( ...
  • 本文实例讲述了Java责任链设计模式。分享给大家供大家参考,具体如下:一 代码abstract class AbstractHandler {private Handler Handler = null;// 持有责任链中下一个责任处理者的引用public void setHandler( ...
  • java设计模式之装饰设计模式详细分析,包含源代码
  • 一、饰器者模式介绍装饰者模式定义:在不改变原有对象的基础上附加功能,相比生成子类更灵活。...类图: 源码分析中的典型应用Java I/O 中的装饰者模式Spring Session 中的装饰者模式Mybatis 缓存中的...
  • 为什么要使用设计模式因为我们的项目的需求是永远在变的,为了应对这种变化,使得我们的代码能够轻易的实现解耦和拓展。如果能够保证代码一次写好以后都不会再改变了,那可以想怎么写怎么写了。如何判断那里需要使用...
  • Java中的设计模式大致可分为三种:行为型,创建型和结构性。顾名思义,知其名,便能明其意。设计模式的出现是为了解决一个在我们周围不断重复发生的问题,以及围绕该问题不断总结和探索出来的核心解决方案。他是一套...
  • 1 前言 设计模式让你和其他开发人员之间有共享的词汇,一旦懂得这些词汇,和其他...4 代码Java) 4.1 定义Observer接口 public interface Observer { //所有的观察者都必须实现update()方法,以实现观察者接口。 /
  • 今天整理了一下已学的几种JAVA设计模式,也是比较经典常用的几种。1单例模式singelton项目开发中往往有这样的需求:某个对象 只需要存在一个(不用new出多个) 即保证该类有且只有一个实例单例模式有两种实现方法(1)...
  • 一、饰器者模式介绍装饰者模式定义:在不改变原有对象的基础上附加功能,相比生成子类更灵活。...类图: 源码分析中的典型应用Java I/O 中的装饰者模式Spring Session 中的装饰者模式Mybatis 缓存中的...
  • 这篇文章就好好地分析一下原型模式。一、认识原型模式1、概念用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。我们拿电脑中复制粘贴的例子来演示一下原型模式.上面这张图已经很明显了,首先我们需要...
  • 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 1.2、优点 * 可以提高程序员的思维能力,编程能力和设计能力。 * 使程序设计更加标准化、代码编制更加工厂化,使软件...
  • 尽管从类设计的视角来看单件模式很简单,但是实现上还是会遇到一些问题,本文着重对这一点来进行分析解决。最简单的单件模式的实现,代码如下:1 /**2 * Created by McBye King on 2016/10/23.3 */4 public ...
  • 2)显示鸭子的信息传统方案解决鸭子问题的分析代码实现1)传统的设计方案(类图)package com.example.demo.dtrategy;public abstract class Duck {public Duck() {}// 显示鸭子信息public abstract voi...
  • 一、需求,针对不同手机类型,不同品牌实现操编码(如打开,电话,关闭等):传统编码:传统方案分析:1)扩展性问题(类爆炸),如果增加手机样式,比如翻盖式,就需要增加各个品牌手机的类,当增加一个品牌,如vivo,也...
  • java设计模式

    2015-08-16 11:34:34
    我希望这本书的阅读者具备...您是一个高级程序员,可以从中全面了解到设计模式以及 Java 的边角技术的使用;您是 一个顶级的系统分析师,可以从中获得共鸣,寻找到项目公共问题的解决办法,呀,是不是把牛吹大了?! 
  • Java设计模式

    2014-07-28 10:48:10
    希望这本书的阅读者具备最基本的代码编写能力,您是一个...您是一个高级程序员,可以从中全面了解到设计模式以及Java 的边角技术的使用;您是 一个顶级的系统分析师,可以从中获得共鸣,寻找到项目公共问题的解决办法

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,876
精华内容 750
热门标签
关键字:

java设计模式代码分析

java 订阅