精华内容
下载资源
问答
  • 所谓设计模式,就是在特定的情况下,应该使用的经过验证的有效的办法。比如,我们系统里有一个对象只允许产生一次,这个时候我们应该Singleton。正如上面所提到的,需要根据场合来使用相应的设计模式。我用过的...
    所谓设计模式,就是在特定的情况下,应该使用的经过验证的有效的办法。比如,我们系统里有一个对象只允许产生一次,这个时候我们应该用Singleton。 正如上面所提到的,需要根据场合来使用相应的设计模式。 我用过的比较多的模式都是关于线程的。当两个线程操作一个队列,一个往里添加,一个往外取值,这个时候很显然, 应该用生产者消费者模式。 具体可以查看下面的URL: http://blog.csdn.net/fantian830211/archive/2006/06/09/784597.aspx
    展开全文
  • JAVA设计模式之单例模式

    万次阅读 多人点赞 2014-04-16 06:51:34
    本文继续介绍23种设计模式系列之单例模式。 概念:  java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。  单例模式有以下特点:  1、单例类...

    本文继续介绍23种设计模式系列之单例模式。

    概念:
      java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。
      单例模式有以下特点:
      1、单例类只能有一个实例。
      2、单例类必须自己创建自己的唯一实例。
      3、单例类必须给所有其他对象提供这一实例。
      单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。


    一、懒汉式单例

     

    //懒汉式单例类.在第一次调用的时候实例化自己 
    public class Singleton {
        private Singleton() {}
        private static Singleton single=null;
        //静态工厂方法 
        public static Singleton getInstance() {
             if (single == null) {  
                 single = new Singleton();
             }  
            return single;
        }
    }

     

    Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。

    (事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)

    但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全,如果你第一次接触单例模式,对线程安全不是很了解,可以先跳过下面这三小条,去看饿汉式单例,等看完后面再回头考虑线程安全的问题:

     

    1、在getInstance方法上加同步

     

    public static synchronized Singleton getInstance() {
             if (single == null) {  
                 single = new Singleton();
             }  
            return single;
    }

     

     

     

    2、双重检查锁定

     

    public static Singleton getInstance() {
            if (singleton == null) {  
                synchronized (Singleton.class) {  
                   if (singleton == null) {  
                      singleton = new Singleton(); 
                   }  
                }  
            }  
            return singleton; 
        }

     

    3、静态内部类

     

    public class Singleton {  
        private static class LazyHolder {  
           private static final Singleton INSTANCE = new Singleton();  
        }  
        private Singleton (){}  
        public static final Singleton getInstance() {  
           return LazyHolder.INSTANCE;  
        }  
    }  

    这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。

     

     

     

     

     

     

    二、饿汉式单例

     

    //饿汉式单例类.在类初始化时,已经自行实例化 
    public class Singleton1 {
        private Singleton1() {}
        private static final Singleton1 single = new Singleton1();
        //静态工厂方法 
        public static Singleton1 getInstance() {
            return single;
        }
    }

    饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

     

     

     

     

    三、登记式单例(可忽略)

    //类似Spring里面的方法,将类名注册,下次从里面直接获取。
    public class Singleton3 {
        private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
        static{
            Singleton3 single = new Singleton3();
            map.put(single.getClass().getName(), single);
        }
        //保护的默认构造子
        protected Singleton3(){}
        //静态工厂方法,返还此类惟一的实例
        public static Singleton3 getInstance(String name) {
            if(name == null) {
                name = Singleton3.class.getName();
                System.out.println("name == null"+"--->name="+name);
            }
            if(map.get(name) == null) {
                try {
                    map.put(name, (Singleton3) Class.forName(name).newInstance());
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            return map.get(name);
        }
        //一个示意性的商业方法
        public String about() {    
            return "Hello, I am RegSingleton.";    
        }    
        public static void main(String[] args) {
            Singleton3 single3 = Singleton3.getInstance(null);
            System.out.println(single3.about());
        }
    }

     登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。 

    这里我对登记式单例标记了可忽略,我的理解来说,首先它用的比较少,另外其实内部实现还是用的饿汉式单例,因为其中的static方法块,它的单例在类被装载的时候就被实例化了。

     

    饿汉式和懒汉式区别

    从名字上来说,饿汉和懒汉,

    饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

    而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

    另外从以下两点再区分以下这两种方式:

     

    1、线程安全:

    饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

    懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。


     

    2、资源加载和性能:

    饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

    而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

    至于1、2、3这三种实现又有些区别,

    第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,

    第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗

    第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。

     

    什么是线程安全?

    如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

    或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。

     

    应用

    以下是一个单例类使用的例子,以懒汉式为例,这里为了保证线程安全,使用了双重检查锁定的方式:

     

    public class TestSingleton {
    	String name = null;
    
            private TestSingleton() {
    	}
    
    	private static volatile TestSingleton instance = null;
    
    	public static TestSingleton getInstance() {
               if (instance == null) {  
                 synchronized (TestSingleton.class) {  
                    if (instance == null) {  
                       instance = new TestSingleton(); 
                    }  
                 }  
               } 
               return instance;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public void printInfo() {
    		System.out.println("the name is " + name);
    	}
    
    }

    可以看到里面加了volatile关键字来声明单例对象,既然synchronized已经起到了多线程下原子性、有序性、可见性的作用,为什么还要加volatile呢,原因已经在下面评论中提到,

    还有疑问可参考http://www.iteye.com/topic/652440
    和http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

     

     

     

    public class TMain {
    	public static void main(String[] args){
    		TestStream ts1 = TestSingleton.getInstance();
    		ts1.setName("jason");
    		TestStream ts2 = TestSingleton.getInstance();
    		ts2.setName("0539");
    		
    		ts1.printInfo();
    		ts2.printInfo();
    		
    		if(ts1 == ts2){
    			System.out.println("创建的是同一个实例");
    		}else{
    			System.out.println("创建的不是同一个实例");
    		}
    	}
    }
    

     运行结果:

    结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。

    对于单例模式的几种实现方式,知道饿汉式和懒汉式的区别,线程安全,资源加载的时机,还有懒汉式为了实现线程安全的3种方式的细微差别。

    更多设计模式:23种设计模式系列

    作者:jason0539

    博客:http://blog.csdn.net/jason0539(转载请说明出处)

    展开全文
  • 常用设计模式总结

    万次阅读 多人点赞 2019-07-31 19:13:12
    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,是可复用面向对象软件的基础。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 ...

    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,是可复用面向对象软件的基础。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。java设计模式广泛认同的有23种,本文介绍最常用的、最会被问到的设计模式。
    #一、设计模式的分类
    总体来说23种设计模式分为三大类:
    创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
    结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
    #二、设计模式的六大原则
    总原则-开闭原则
    对扩展开放,对修改封闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。
    想要达到这样的效果,我们需要使用接口和抽象类等,后面的具体设计中我们会提到这点。
    1、单一职责原则
    不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,否则就应该把类拆分。
    2、里氏替换原则(Liskov Substitution Principle)
    任何基类可以出现的地方,子类一定可以出现。里氏替换原则是继承复用的基石,只有当衍生类可以替换基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
    里氏代换原则是对“开-闭”原则的补充。实现“开闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。里氏替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。
    3、依赖倒转原则(Dependence Inversion Principle)
    面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。
    4、接口隔离原则(Interface Segregation Principle)
    每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。
    5、迪米特法则(最少知道原则)(Demeter Principle)
    一个类对自己依赖的类知道的越少越好。无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。
    最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。
    6、合成复用原则(Composite Reuse Principle)
    尽量首先使用合成/聚合的方式,而不是使用继承。
    #常用设计模式
    ##1、工厂方法模式
    工厂方法模式分为三种:
    1.1、普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
    举例如下:(我们举一个发送邮件和短信的例子)

    public interface Sender {  
        public void Send();  
    }  
    public class MailSender implements Sender {  
        @Override  
        public void Send() {  
            System.out.println("this is mailsender!");  
        }  
    }  
    public class SmsSender implements Sender {  
      
        @Override  
        public void Send() {  
            System.out.println("this is sms sender!");  
        }  
    }  
    public class SendFactory {  
      
        public Sender produce(String type) {  
            if ("mail".equals(type)) {  
                return new MailSender();  
            } else if ("sms".equals(type)) {  
                return new SmsSender();  
            } else {  
                System.out.println("请输入正确的类型!");  
                return null;  
            }  
        }  
    }  
    public class FactoryTest {  
      
        public static void main(String[] args) {  
            SendFactory factory = new SendFactory();  
            Sender sender = factory.produce("sms");  
            sender.Send();  
        }  
    }  
    

    输出:this is sms sender!
    2.2、多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。
    将上面的代码做下修改,改动下SendFactory类就行,如下:

    public class SendFactory {  
          
        public Sender produceMail(){  
            return new MailSender();  
        }  
          
        public Sender produceSms(){  
            return new SmsSender();  
        }  
    }  
    public class FactoryTest {  
      
        public static void main(String[] args) {  
            SendFactory factory = new SendFactory();  
            Sender sender = factory.produceMail();  
            sender.Send();  
        }  
    }  
    

    输出:this is mailsender!
    3.3、静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

    public class SendFactory {  
          
        public static Sender produceMail(){  
            return new MailSender();  
        }  
          
        public static Sender produceSms(){  
            return new SmsSender();  
        }  
    }  
    public class FactoryTest {  
      
        public static void main(String[] args) {      
            Sender sender = SendFactory.produceMail();  
            sender.Send();  
        }  
    }  
    

    输出:this is mailsender!
    总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。
    ##2、抽象工厂模式
    工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

    public interface Sender {  
        public void Send();  
    }  
    public class MailSender implements Sender {  
        @Override  
        public void Send() {  
            System.out.println("this is mailsender!");  
        }  
    }  
    public class SmsSender implements Sender {  
      
        @Override  
        public void Send() {  
            System.out.println("this is sms sender!");  
        }  
    }  
    public class SendMailFactory implements Provider {  
          
        @Override  
        public Sender produce(){  
            return new MailSender();  
        }  
    }  
    public class SendSmsFactory implements Provider{  
      
        @Override  
        public Sender produce() {  
            return new SmsSender();  
        }  
    }  
    public interface Provider {  
        public Sender produce();  
    }  
    public class Test {  
      
        public static void main(String[] args) {  
            Provider provider = new SendMailFactory();  
            Sender sender = provider.produce();  
            sender.Send();  
        }  
    }  
    

    其实这个模式的好处就是,如果你现在想增加一个功能:发及时信息,则只需做一个实现类,实现Sender接口,同时做一个工厂类,实现Provider接口,就OK了,无需去改动现成的代码。这样做,拓展性较好!
    ##3、单例模式
    java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍经常被面试的经典的两种:懒汉模式、饿汉模式。
    单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。比如每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。
    ###3.1、懒汉式单例

    //懒汉式单例类.在第一次调用的时候实例化自己 
    public class Singleton {
        private Singleton() {}
        private static Singleton single=null;
        //静态工厂方法 
        public static Singleton getInstance() {
             if (single == null) {  
                 single = new Singleton();
             }  
            return single;
        }
    } 
    

    Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。
    (事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)
    但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全。
    1、在getInstance方法上加同步

    public static synchronized Singleton getInstance() {
             if (single == null) {  
                 single = new Singleton();
             }  
            return single;
    }
    

    2、双重检查锁定

    public static Singleton getInstance() {
            if (singleton == null) {  
                synchronized (Singleton.class) {  
                   if (singleton == null) {  
                      singleton = new Singleton(); 
                   }  
                }  
            }  
            return singleton; 
    }
    

    3、静态内部类

    public class Singleton {  
        private static class LazyHolder {  
           private static final Singleton INSTANCE = new Singleton();  
        }  
        private Singleton (){}  
        public static final Singleton getInstance() {  
           return LazyHolder.INSTANCE;  
        }  
    }  
    

    这种方式是当被调用getInstance()时才去加载静态内部类LazyHolder,LazyHolder在加载过程中会实例化一个静态的Singleton,因为利用了classloader的机制来保证初始化instance时只有一个线程,所以Singleton肯定只有一个,是线程安全的,这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。

    ###3.2、饿汉式单例

    //饿汉式单例类.在类初始化时,已经自行实例化 
    public class Singleton1 {
        private Singleton1() {}
        private static final Singleton1 single = new Singleton1();
        //静态工厂方法 
        public static Singleton1 getInstance() {
            return single;
        }
    }
    

    饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

    ###3.3、饿汉式和懒汉式区别
    从名字上来说,饿汉和懒汉,
    饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,
    而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。
    另外从以下两点再区分以下这两种方式:
    1、线程安全:
    饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,
    懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。
    2、资源加载和性能:
    饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,
    而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
    至于1、2、3这三种实现又有些区别,
    第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,
    第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗
    第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。
    综上大部分情况下单例模式中最好的实现方式就是静态内部类的懒汉模式。
    ##4、建造者模式
    一、场景
    当需要生产一辆汽车时,我们需要为其装配发动机、轮胎、座椅等等部件,这个装配过程是比较复杂的而且也需要较高的组装技术。而建造者模式(Builder Pattern)就是为了将部件与组装分离开。
    二、 概念
      将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
      与抽象工厂的区别:在建造者模式里,有个指导者,由指导者来管理建造者,用户是与指导者联系的,指导者联系建造者最后得到产品。即建造模式可以强制实行一种分步骤进行的建造过程。
      建造模式是将复杂的内部创建封装在内部,对于外部调用的人来说,只需要传入建造者和建造工具,对于内部是如何建造成成品的,调用者无需关心。
    三、建造者模式结构组成
    Product: 表示被构造的复杂对象,其中包含需要构建的部件属性。
    Builder: 创建一个产品对象的各个部件指定抽象接口。
    ConcreteBuilder: 实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示。
    Director: 调用具体建造者角色以创建产品对象。
    下面以构建一辆汽车为例,写示例代码:
    1.Product角色:组装一辆汽车首先的有各种配件,如发动机、轮胎、座椅等。

    public class Car{
        public String engine;
        public String tyre;
        public String seat;
        public Car(){
    
        }
    
        public String getEngine() {
            return engine;
        }
        public void setEngine(String engine) {
            this.engine = engine;
        }
        public String getTyre() {
            return tyre;
        }
        public void setTyre(String tyre) {
            this.tyre = tyre;
        }
        public String getSeat() {
            return seat;
        }
        public void setSeat(String seat) {
            this.seat = seat;
        }
    
    }
    

    2.Builder角色:知道了所需配件后,就需要生产配件了,定义一个生产配件的抽象建造者接口。

    public interface Builder {
        String buildEngine();
        String buildTyre();
        String buildSeat();
    }  
    

    3.ConcreteBuilder角色:实现抽象的 建造者接口生成具体的建造者,并开始生产具体的配件。

    public class CarBuilder implements Builder{
    
        @Override
        public String buildEngine() {
            // 生产发动机
            return "发动机";
        }
    
        @Override
        public String buildTyre() {
            // 生产轮胎
            return "轮胎";
        }
    
        @Override
        public String buildSeat() {
            // 生产座椅
            return "座椅";
        }
    }
    
    }   
    

    4.Director角色:在生产出配件后,由指导者指导组装配件生成汽车。

    public class CarDirector {
        CarBuilder cb;
        public CarDirector(CarBuilder cb){
            this.cb=cb;
        }
        public Car constructCar(){
            Car car=new Car();
            car.setEngine(cb.buildEngine());
            car.setTyre(cb.buildTyre());
            car.setSeat(cb.buildSeat());
            return car;
        }
    }
    

    5.最终得到一辆汽车:

    public class Client {
    
        public static void main(String[] args) {
            CarDirector carDirector=new CarDirector(new CarBuilder());
            Car car=carDirector.constructCar();
            System.out.println(car.getEngine()+car.getTyre()+car.getSeat());
    
        }
    }
    

    ##5、适配器模式
    适配器模式,把一个类接口变化成客户端所期待的另一个类的接口,使原来因接口不匹配而无法一起工作的类能够一起工作。Java源码中的例子:如Java IO中的java.io.InputStreamReader(InputStream) 和java.io.OutputStreamWriter(OutputStream)就是典型的适配器模式,通过InputStreamReader、OutputStreamWriter适配器将字节流转换为字符流。
    ###5.1、类适配器
    Target(目标角色): 客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
    Adaptee(源角色):现在需要适配的类。
    Adapter(适配器): 适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。

    /**
     * 目标角色,如举例中需要转换成的三相插头
     *  @author  14501_000
     */
    public interface Target {
         void handleReq();
    }   
    
    /**
     * 源角色,需要被适配的类,如举例中的两脚插头
     */
    public class Adaptee {
         public void request(){
            System.out.println( "可以完成客户请求的需要的功能!" );
        }
    }   
    
    /**
     * 适配器,把源接口转换成目标接口,即将两脚插头转换为三脚插头
     *
     */
    public class Adapter extends Adaptee implements Target{
         public void handleReq() {
             super.request();
        }
         }   
    
    /**
     * 客户端类,通过三脚插座进行工作
     *
     */
    public class Client {
         public void work(Target t){
             t.handleReq();
        }
         public static void main(String[] args){
            Client c = new Client();
            Target t = new Adapter();
             c.work(t);
        }
    }
    

    ###5.2、对象适配器
    上面这种实现的适配器称为类适配器,因为 Adapter 类既继承了 Adaptee (被适配类),也实现了 Target 接口(因为 Java 不支持多继承,所以这样来实现),在 Client 类中我们可以根据需要选择并创建任一种符合需求的子类,来实现具体功能。另外一种适配器模式是对象适配器,它不是使用多继承或继承再实现的方式,而是使用直接关联,或者称为委托的方式,通过组合的方式跟适配对象组合。

    /**
     * 适配器,把源接口转换成目标接口,即将两脚插头转换为三脚插头
     *
     */
    public class Adapter implements Target{
        Adaptee adaptee ;
         public Adapter(Adaptee adaptee){
             this.adaptee = adaptee ;
        }
         public void handleReq() {
             adaptee.request();
        }
    
    }
    
    public class Client {
         public void work(Target  t){
             t.handleReq();
        }
         public static void main(String[]  args ) {
            Client c =new Client();
            Adaptee adaptee =new Adaptee();
            Target t = new Adapter(adaptee);
             c.work( t );
        }
    }
    

    测试结果与上面的一致。 使用对象适配器模式,可以使得 Adapter 类(适配类)根据传入的 Adaptee 对象达到适配多个不同被适配类的功能,当然,此时我们可以为多个被适配类提取出一个接口或抽象类。这样看起来的话,似乎对象适配器模式更加灵活一点。
    ###5.3、适配器模式适用场景
    系统需要使用现有的类,而这些类的接口不符合系统的接口。
    想要建立一个可以重用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
    两个类所做的事情相同或相似,但是具有不同接口的时候。
    旧的系统开发的类已经实现了一些功能,但是客户端却只能以另外接口的形式访问,但我们不希望手动更改原有类的时候。
    ##6、装饰者模式
    装饰(Decorate)模式又称为包装(Wrapper)模式。可以动态的为一个对象增加新的功能。装饰模式是一种用于代替继承的技术,无须通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。
    举一个简单的汽车例子,创造每一种功能的汽车都需要继承车的父类进行实现,那么当我们需要既能路上行驶又能水上行驶的车又得继续继承父类拓展新的类。所以每增加一种新功能的汽车都需要新增一个类,这样的话就会创建大量的类。这时候就能使用装饰模式了。
    ###6.1、示例
    抽象构件

    public interface AbstractCar {
        void move();
    }  
    

    具体构建

    public class Car implements AbstractCar{
    
        public void move() {
            System.out.println("路上行驶");
        }
    }
    

    装饰角色

    public class SuperCar implements AbstractCar{
        protected AbstractCar car;
        public SuperCar(AbstractCar car){
            this.car=car;
        }
    
        public void move() {
            car.move();
        }
    
    }
    

    具体装饰 角色

    /**
     * 飞行汽车  
    */   
    
    ublic class FlyCar extends SuperCar{
    
        public FlyCar(AbstractCar car) {
            super(car);
        }
    
        public void fly() {
            System.out.println("空中行驶汽车");
        }
    
    
        @Override
        public void move() {
            super.move();
             fly();
        }
    
    }
    
    
    
    /**
     * 水上汽车
     */
    public class WaterCar extends SuperCar{
    
        public WaterCar(AbstractCar car) {
            super(car);
        }
    
        public void swim() {
            System.out.println("水上行驶汽车");
        }
        @Override
        public void move() {
            super.move();
            swim();
        }
    
    }
    

    客户端

    public class Client {
    
        public static void main(String[] args) {
            Car car=new Car();
            car.move();
    
            System.out.println("------增加新功能,飞行------");
            FlyCar flyCar=new FlyCar(car);
            flyCar.move();
    
            System.out.println("------新增加功能,水上行驶-----");
            WaterCar waterCar=new WaterCar(car);
            waterCar.move();
    
            System.out.println("-----新增加两个功能,飞行与水上行驶-----");
            WaterCar waterCar2=new WaterCar(flyCar);
            waterCar2.move();
    
        }
    
    }
    

    //输出结果
    路上行驶
    ------增加新功能,飞行------
    路上行驶
    空中行驶汽车
    ------新增加功能,水上行驶-----
    路上行驶
    水上行驶汽车
    -----新增加两个功能,飞行与水上行驶-----
    路上行驶
    空中行驶汽车
    水上行驶汽车
    由此可知,使用装饰模式就不用创建大量新的类而可以拓展出具有更多功能的对象了。
    ###6.2、装饰模式在Java I/O库中的应用
    IO流实现细节:
    Component抽象构件角色:io流中的InputStream,OutputStream,Reader,Writer
    ConcreteComponent具体构件角色:io流中的FileInputStream,FileOutputStream
    Decorate装饰角色:持有抽象构件的引用,FilterInputStream,FilterOutputStream
    ConcreteDecorate具体装饰角色:负责给构件对象添加新的责任,BufferedInputStream,BufferedOutputStream等
    ##7、代理模式
    为某个对象提供一个代理,从而控制这个代理的访问。代理类和委托类具有共同的父类或父接口,这样在任何使用委托类对象的地方都可以使用代理类对象替代。代理类负责请求的预处理、过滤、将请求分配给委托类处理、以及委托类处理完请求的后续处理。
    ###7.1、静态代理
    由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
    示例代码:
    1.抽象角色

    public interface AbstractSubject {
         void doSomething();
    }   
    

    2.代理角色

    public class ProxySubject implements AbstractSubject{
         private AbstractSubject  real ;
         public ProxySubject(AbstractSubject  real) {
             this.real=real ;
        }
         @Override
         public void doSomething() {
    
             real.doSomething();
        }
    
         public void doOtherthing() {
    
        }
    }   
    

    3.真实角色

    public class RealSubject implements AbstractSubject{
         @Override
         public void doSomething() {
            System.out.println( "真实角色被使用" );
        }
    }   
    

    4.客户端

    public class Client {
         public static void main(String[]  args ) {
            RealSubject real = new  RealSubject();
            ProxySubject proxy = new  ProxySubject( real );
             proxy.doSomething();
        }
    }   
    

    5.静态代理的优缺点
    优点: 业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
    缺点:代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
    ###7.2、动态代理
    动态代理类的源码是程序在运行期间由JVM根据反射等机制动态生成的,所以不存在代理类的字节码文件。代理角色和真实角色的联系在程序运行时确定。
    1.首先看看和动态代理相关JavaAPI
    ① .java.lang.reflect.Proxy
    这是Java动态代理机制生成的所有代理类的父类,它提供了一组静态的方法来为一组接口动态的生成代理类及其对象。
    Proxy类的静态方法:

    //方法 1: 该方法用于获取指定代理对象所关联的调用处理器  
    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 )   
    

    ②.java.lang.reflect.InvocationHandler
    这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理类对象时都要指定一个对应的调用处理器对象。
    InvocationHandler核心方法

    //该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象  
    //第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行   
    Object invoke(Object proxy, Method  method,Object[] args )     
    

    ③ .java.lang.reflect.ClassLoader
      这是类装载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。每次生成动态代理类对象时都需要指定一个类装载器对象 。
    2.动态代理实现步骤

    • 实现InvocationHandler接口创建自己的调用处理器 。
    • 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类 。
    • 执行真实角色具体任务。

    示例代码
    1.抽象角色和真实角色代码与上述相同 。
    2. 创建自己的调用处理器:

    public class SubjectHandler implements InvocationHandler{
        AbstractSubject real;
        public SubjectHandler(AbstractSubject real){
            this.real=real;
        }
    
        @Override
        public Object invoke(Object obj, Method method, Object[] args)throws Throwable {
            System.out.println("代理类预处理任务");
            //利用反射机制将请求分派给委托类处理。Method的invoke返回Object对象作为方法执行结果。  
      //因为示例程序没有返回值,所以这里忽略了返回值处理
            method.invoke(real, args);
            System.out.println("代理类后续处理任务");
            return null;
        }
    
    }
    

    3.客户端 :

    public class Client {
    
        public static void main(String[] args) {
            RealSubject real=new RealSubject();
            SubjectHandler handler=new SubjectHandler(real);
            //生成代理类对象
            AbstractSubject proxy=(AbstractSubject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{AbstractSubject.class},handler);
            proxy.doSomething();
    
        }
    
    }
    

    4.动态代理的优缺点
    优点:
      动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
    不足:
      Proxy 已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持 interface 代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫 Proxy。Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理,原因是多继承在 Java 中本质上就行不通。
    ##8、策略模式
    下面是某个市场人员接到单后的报价策略,报价策略很复杂,可以简单作如下分类:
    普通客户小批量,不打折
    普通客户大 批量,打9折
    老 客户小批量,打8.5折
    老 客户大批量,打8折
    我们通常可以适用条件语句进行处理,代码如下:

    public class Test {
        public double getPrice(String type ,double price ){
            if(type.equals("普通客户小批量")){
                System.out.println("不打折");
                return price;
            }else if(type.equals("普通客户大批量")){
                System.out.println("打9折");
                return price*0.9;
    
            }else if(type.equals("老客户小批量")){
                System.out.println("打8.5折");
                return price*0.85;
    
            }else if(type.equals("老客户大批量")){
                System.out.println("打8折");
                return price*0.8;
            }
            return price;
        }
    }
    

    这样实现起来比较简单,符合一般开人员的思路,但是当类型特别多,算法比较复杂时,整个条件控制代码会变得很长,难于维护。这时我们可以采用策略模式,将不同的策略分开,委派给不同的对象管理。
    使用策略模式实现:

    public interface Strategy {
        /**
         * 
         * @param price 商品原价
         * @return 打折后价格
         */
        public double getPrice(double price);
    }
    
    
    public class NewCustomerFewStrategy implements Strategy{
    
        public double getPrice(double price) {
            System.out.println("普通客户小批量,不打折");
            return price;
        }
    }
    
    public class NewCustomerManyStrategy implements Strategy{
    
        public double getPrice(double price) {
            System.out.println("普通客户大批量,打9折");
            return price*0.9;
        }
    }
    
    public class OldCustomerFewStrategy implements Strategy{
    
        public double getPrice(double price) {
            System.out.println("老客户小批量,打8.5折");
            return price*0.85;
        }
    }
    
    public class OldCustomerManyStrategy implements Strategy{
    
        public double getPrice(double price) {
            System.out.println("老客户大批量,打8折");
            return price;
        }
    } 
    
    
    public class Context {
        private Strategy strategy;//持有策略引用
    
        public Context(Strategy strategy) {
            super();
            this.strategy = strategy;
        }
        public void printPrice(double price ){
            System.out.println("价格为:"+strategy.getPrice(price));
        }
    }
    
    
    
    public class Client {
         public static void main(String[] args) {
            Strategy strategy= new NewCustomerFewStrategy();
            Context context= new Context(strategy);
            context.printPrice(100);
        }
    
    }  
    
    //输出结果
    //普通客户小批量,不打折
    //价格为:100.0  
    
    

    从上面的示例可以看出,策略模式仅仅封装算法,提供新的算法插入到已有系统中,以及老算法从系统中“退休”的方法,策略模式并不决定在何时使用何种算法。在什么情况下使用什么算法是由客户端决定的。
    优点:
    策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。
    使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。
    缺点:
    客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。
    由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。
    ##9、模板方法模式
    模板方法模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。
    比如定义一个操作中的算法的骨架,将步骤延迟到子类中。模板方法使得子类能够不去改变一个算法的结构即可重定义算法的某些特定步骤。
    模式中的角色:

    • 抽象类(AbstractClass):实现了模板方法,定义了算法的骨架。
    • 具体类(ConcreteClass):实现抽象类中的抽象方法,已完成完整的算法。

    以准备去学校所要做的工作(prepareGotoSchool)为例,假设需要分三步:穿衣服(dressUp),吃早饭(eatBreakfast),带上东西(takeThings)。学生和老师要做得具体事情肯定有所区别。
    抽象类AbstractClass

    public abstract class AbstractPerson{  
         //抽象类定义整个流程骨架  
         public void prepareGotoSchool(){  
              dressUp();  
              eatBreakfast();  
              takeThings();  
         }  
         //以下是不同子类根据自身特性完成的具体步骤  
         protected abstract void dressUp();  
         protected abstract void eatBreakfast();  
         protected abstract void takeThings();  
    } 
    

    具体类ConcreteClass

    public class Student extends AbstractPerson{  
         @Override  
         protected void dressUp() {  
              System.out.println(“穿校服");  
         }  
         @Override  
         protected void eatBreakfast() {  
              System.out.println(“吃妈妈做好的早饭");  
         }  
      
         @Override  
         protected void takeThings() {  
              System.out.println(“背书包,带上家庭作业和红领巾");  
         }  
    }  
    
    public class Teacher extends AbstractPerson{  
         @Override  
         protected void dressUp() {  
              System.out.println(“穿工作服");  
         }  
         @Override  
         protected void eatBreakfast() {  
              System.out.println(“做早饭,照顾孩子吃早饭");  
         }  
      
         @Override  
         protected void takeThings() {  
              System.out.println(“带上昨晚准备的考卷");  
         }  
    }  
    
    public class Client {  
         public static void main(String[] args) {  
         Student student = new Student()  
         student.prepareGotoSchool();  
      
         Teacher teacher  = new Teacher()  
         teacher.prepareGotoSchool();  
         }  
    }  
    

    优点:
    模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
    子类实现算法的某些细节,有助于算法的扩展。
    通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。
    缺点:
    每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。
    适用场景:
    在某些类的算法中,用了相同的方法,造成代码的重复。
    控制子类扩展,子类必须遵守算法规则。
    ##10、观察者模式
    观察者模式定义了一个一对多的依赖关系,让多个观察者对象同时监听同一个主题对象。当这个主题状态发生改变时,会通知所有观察者对象,让它们自动更新自己。
    ###10.1、模型结构

    • 抽象主题角色(Subject): 把所有对观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
    • 具体主题角色(ConcreteSubject): 在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个子类实现。
    • 抽象观察者角色(Observer): 为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。
    • 具体观察者角色(ConcreteObserver): 该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。
      这里写图片描述

    ###10.2、代码示例
    抽象主题角色:

    public class Subject {
        protected List<Observer> list=new ArrayList<Observer>();
    
        public void registerObserve(Observer obs){
            list.add(obs);
        }
    
        public void removeObserve(Observer obs){
            list.remove(obs);
        }
    
        //通知所有的观察者更新状态
        public void notifyAllObserve(){
            for(Observer obs:list){
                obs.update(this);
            }
        }
    }
    

    具体 主题角色:

    public class ConcreteSubject extends Subject{
        private int state;
    
        public int getState() {
            return state;
        }
    
        //主题对象发生变化,通知所有观察者
        public void setState(int state) {
            this.state = state;
            this.notifyAllObserve();
        }
    }
    

    抽象观察者角色:

    public interface Observer {
        void update(Subject sub);
    }
    

    具体观察者角色:

    public class ConcreteObserver implements Observer{
        private int myState;//与目标对象state值保持一致
    
        public void update(Subject sub) {
            myState=((ConcreteSubject)sub).getState();
            System.out.println("观察者得到的值:"+myState);
        }
    }  
    

    客户端 :

    public class Client {
    
        public static void main(String[] args) {
            //目标对象
            ConcreteSubject cs=new ConcreteSubject();
    
            //创建多个具体观察者
            ConcreteObserver observe1=new ConcreteObserver();
            ConcreteObserver observe2=new ConcreteObserver();
            ConcreteObserver observe3=new ConcreteObserver();
    
            //注册观察者
            cs.registerObserve(observe1);
            cs.registerObserve(observe2);
            cs.registerObserve(observe3);
    
            //改变被观察者状态
            cs.setState(2);
    
        }
    }
    

    输出结果
    观察者得到的值:2
    观察者得到的值:2
    观察者得到的值:2
    ###10.3、推模式与拉模式
    推模式:每次都会把通知以广播的方式发送给所有观察者,所有观察者只能被动接收, 推送的信息通常是主题对象的全部或部分数据 。
    拉模式: 主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了 。
    比较: 推模式是假定主题对象知道观察者需要的数据;而拉模式是主题对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传递给观察者,让观察者自己去按需要取值。
    ###10.4、Java自带对观察者模式的支持
    JavaSE中提供了java.util.Observable和java.util.Observer来实现观察者模式。
    代码示例:
    具体目标对象:

    public class ConcreteSubject extends Observable{
        private int state;
    
        public int getState() {
            return state;
        }
        public void setState(int state) {
            this.state = state;//目标对象状态发生改变
    
            setChanged();//表示目标对象已经做了更改
            notifyObservers(state);//通知所有观察者
        }
    }
    1
    2
    3
    4
    

    具体观察者:

    public class ConcreteObserver implements Observer{
        private int mystate;
        public void update(Observable o, Object arg) {
            mystate=((ConcreteSubject)o).getState();
            System.out.println("观察者接收到的状态:"+mystate);
        }
    }  
    

    客户端:

    public class Client {
        public static void main(String[] args) {
            //创建具体主题
            ConcreteSubject cs=new ConcreteSubject();
    
            //创建观察者
            ConcreteObserver observer1=new ConcreteObserver();
            ConcreteObserver observer2=new ConcreteObserver();
            ConcreteObserver observer3=new ConcreteObserver();
    
            //将观察者加入到目标对象观察者集合
            cs.addObserver(observer1);
            cs.addObserver(observer2);
            cs.addObserver(observer3);
    
            //目标对象改变
            cs.setState(100);
        }
    }
    

    输出结果:
    观察者接收到的状态:100
    观察者接收到的状态:100
    观察者接收到的状态:100


    上面的内容借鉴了很多博主的文章

    展开全文
  • 用过设计模式 -- 导航

    千次阅读 2021-02-11 08:39:22
  • 对于设计模式,相信大多数人都有了解,或为了面试,或为了实际开发,但是对于大多数人来说,实际开发中,真正用设计模式的地方,少之又少。最主要的原因,还是因为我们对设计模式并未真正的理解。那么,如何理解设计...
  • 上一篇文章我们讲了单例模式的 8 种实现方式以及...知道哪些设计模式?它的使用场景有哪些?它们有哪些优缺点? 典型回答 设计模式从大的维度来说,可以分为三大类:创建型模式、结构型模式及行为型模式,这三大类下
  • 设计模式之单例设计模式

    万次阅读 多人点赞 2021-03-21 20:28:27
    1 设计模式(Design pattern) 代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一...
  • Spring中用了哪些设计模式

    千次阅读 2019-03-01 13:40:30
    设计模式作为工作学习中的枕边书,却时常处于勤说不用的尴尬境地,也不是我们时常忘记,只是一直没有记忆。 Spring作为业界的经典框架,无论是在架构设计方面,还是在代码编写方面,都堪称行内典范。好了,话不多说...
  • 23种设计模式汇总整理

    万次阅读 多人点赞 2015-04-09 10:57:11
    设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式 结构型模式,共七种:适配器模式、装饰者模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 ...
  • C现代编程 集成开发环境 设计模式 极限编程 测试驱动开发 重构 C Qt设计模式电子工业出版社9787121168901[美]艾朱斯特 C嵌入式设计模式 二、C++ 道法自然-面向对象实践指南 设计模式:可复用面向对象软件的基础...
  • C++ 设计模式

    万次阅读 多人点赞 2018-02-09 09:26:25
    设计模式代表了最佳的实践,在面向对象的编程中被很多老鸟们反复使用使用设计模式有很多好处:可重用代码、保证代码可靠性、使代码更易被他人理解 ......
  • 设计模式

    千次阅读 多人点赞 2019-07-22 09:33:22
    设计模式简介 设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件...使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性...
  • 用过设计模式(7)--享元模式

    千次阅读 多人点赞 2020-07-02 21:42:58
    可能桥接模式是最抽象的设计模式,但是享元模式我觉得是最烦的设计模式了。 因为这个模式和==“池技术”有着密不可分==的联系。 享元模式与池技术 说到享元模式,第一个想到的应该就是池技术了,String常量池、...
  • 所知道的设计模式哪些 Java中一般认为有23 种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。下面列出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多越好。 ...
  • 本章就深入分析一下,jdbc里使用到了哪些优秀的设计模式呢? 2、栗子 大家耳熟能详的听23种设计模式。但是我估计大家最熟悉的是23这个数字,而不太熟悉内部的实现或者说是内容 1、静态工厂方法 ...
  • 设计模式就是经过前任无数次的实践总结出的,设计过程中可以反复使用的,可以解决特定问题的设计方法。 二、常用的设计模式哪些? 1、单例模式(懒汉式、饿汉式) 步骤: 1、构造方法私有化,让除了自己类能创建,...
  • 用过设计模式(6)-- 门面模式

    千次阅读 多人点赞 2021-02-09 15:15:58
    门面就是让一看就知道里面可以提供什么东西,但是又不会知道它是如何提供的。 门面模式是什么? 我知道,这张图也看不明白在讲什么。 门面模式的定义已经呼之欲出了:要求一个子系统的外部与其内部的通信必须...
  • 所知道的设计模式哪些

    千次阅读 2018-08-27 22:41:03
    Java 中一般认为有 23 种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。下面列出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多越好。 总体来说设计模式分为三...
  • 1.单例模式(singleton) 2.简单工厂(StaticFactory Method) 3.工厂方法(Factory Method)和抽象工厂(Abstract Factory) 4.代理模式(Proxy) 5.命令模式(Command) 6. 策略模式(Strategy) 7.门面模式(Facade) 8.桥接模式...
  • 设计模式概述 20世纪80年代,四人组(Gang of Four orGoF)将常用的23种软件设计模式进行了归纳整理,自此标志着软件设计模式的正式诞生。它旨在“模式”来统一和沟通...设计模式是一套被反复使用的、经过合理分...
  • 为什么使用设计模式——设计模式

    千次阅读 2018-08-26 13:50:16
    Christopher Alexander说:每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心,这样,就能一次又一次地使用该方案而不必做重复劳动。这个思想同样适用于面向对象的设计模式,核心...
  • 用过设计模式(10)-- 命令模式

    千次阅读 多人点赞 2021-02-10 23:18:07
    咱也没读什么书,看网上的命令模式讲的那叫个花里胡哨,看来看去,我接收到的讯息如下: 命令请求者 命令调用者 命令储存 命令回撤 这是什么?这,我直接想到了消息队列好吧。还要我怎样? 看一下命令模式使用...
  • 用过设计模式(2)-- 单例模式

    千次阅读 多人点赞 2021-02-07 13:29:22
    文章目录单例模式代码实现提升部分多线程下的单例模式饿汉式单例懒汉还是饿汉?单例模式的优缺点 单例模式 什么是单例模式呢? 在项目中,有些类是需要对它们进行“计划生育”的,即这个类只能有一个实例,如果出现...
  • 设计模式总结

    万次阅读 多人点赞 2014-01-18 10:49:06
    刚刚接触java没多久就在学长那里听过设计模式的大名,但是由于能力有限,一直不敢触碰。而今有幸将其都认识了。 2、开始有设计的理论了。在接触设计模式之前没有怎么想过设计方面东东,看到问题就立马动手解决,...
  • 用过设计模式(5)-- 中介者模式

    千次阅读 多人点赞 2021-02-08 22:23:01
    文章目录前言中介者模式缺点应用场景案例:大型相亲现场 VS 传统媒人模式中介者模式类图...又叫调停者模式,但是我更喜欢中介者这个名字,因为一下就表达出这个设计的意图了。 一个中介对象封装一系列对象交互,中介.
  • 在真实的业务场景中,有用什么设计模式来编写更优雅的代码吗? 我们更多的是每天从产品经理那里接受到新需求后,就开始MVC一把梭,面向sql编程了。 我们习惯采用MVC架构,实时上是非常容易创建很多贫血对象模型...
  • Java IO中涉及到哪些设计模式
  • 设计模式简述

    千次阅读 多人点赞 2019-05-23 17:46:14
    设计模式简述 什么是设计模式 为什么要用设计模式 设计模式的由来 设计模式的六大原则 设计模式分类

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 853,632
精华内容 341,452
关键字:

你用过哪些设计模式