精华内容
下载资源
问答
  • 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


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

    展开全文
  • java策略模式典型案例

    万次阅读 多人点赞 2019-05-25 00:32:21
    以一个顾客价格计算策略为背景,写一个策略模式的demo 参考代码 :https://github.com/zhang-xiaoxiang/DesignPatterns23 没有用策略模式我们一般是下面的写法,直接写一个类,在类里面直接写策略算法(功能实现) //...

    以一个顾客价格计算策略为背景,写一个策略模式的demo参考代码 : https://github.com/zhang-xiaoxiang/DesignPatterns23

    日常碰到的业务概述

    登录类型,支付类型,供应商渠道,不同等级会员享受的优惠券价格不一样,等等业务判断,大量if else导致拓展(侧重新增)极其困难,维护(侧重修改)自然是改起来头痛(其实一个类型的增加[拓展一个类型]往往对应这个类型的增删改查CRUD[维护]),比如业务一开始一个简单的登录,往往做一个电话号码和验证码登录的方式或者账号密码登录方式,后来随着业务的增加或者提高用户体验,那么需要拓展(新增一种)三方登录,比如新增微信登录,支付宝登录,甚至抖音登录,一大堆,你要根据登录方式来处理,那就有点恼火吧,支付方式也是同样的问题,我们可以发现一个规律,凡是可以枚举的业务,往往都需要使用设计模式才能更好的解决,比如策略模式(往往搭配工厂模式使用更配哦),水来土掩,兵来将挡,这思想和高中数学中的分类讨论思想一模一样.遇事不要慌,因为还有dang中央.所以,我们就打个栗子,举个比方,更加形象一点,

    没有用策略模式

    我们一般是下面的写法,直接写一个类,在类里面直接写策略算法(功能实现)

    //package com.demo.strategy;
    
    /**
     * NoStrategy:没有策略的做法
     * 实现起来比较容易,符合一般开发人员的思路
     * 假如,类型特别多,算法比较复杂时,整个条件语句的代码就变得很长,难于维护。
     * 如果有新增类型,就需要频繁的修改此处的代码!
     * 不符合开闭原则!---对这个类的修改要关闭,就是这个类要是写好了就不要去改他了,对类的功能的拓展要开放,显然只有面向接口编程才满足,
     * 所以策略模式Strategy这个接口(文中涉及到的)就应运而生了晒哈哈哈
     * @author zhangxiaoxiang
     * @date: 2019/05/24
     */
    public class NoStrategy {
    	/**
    	 * 传入客服等级类型获取相应的价格
    	 * @param type   会员类型(等级)
    	 * @param price  响应的价格
    	 * @return
    	 */
    	public double getPrice(String type, double price) {
    
    		if ("普通客户小批量".equals(type)) {
    			System.out.println("[未采用设计模式] 不打折,原价");
    			return price;
    		} else if ("普通客户大批量".equals(type)) {
    			System.out.println("[未采用设计模式] 打九折");
    			return price * 0.9;
    		} else if ("老客户小批量".equals(type)) {
    			System.out.println("[未采用设计模式] 打八折");
    			return price * 0.8;
    		} else if ("老客户大批量".equals(type)) {
    			System.out.println("[未采用设计模式] 打七折");
    			return price * 0.7;
    
    
    		//拓展一种策略
    		}else if("老客户特大批量".equals(type)){
    			System.out.println("[未采用设计模式] 打六折");
    			return price*0.6;
    		}
    
    
    		//乱传的也是当普通客户小批量(就是不打折)
    		return price;
    	}
    
    }
    

    策略模式

    看到上面的类,貌似想到一句话是不是,面向接口编程,上来就应该先是一个接口,所以改成接口

    1:面向接口编程,策略模式也是一样,上来先来个策略接口压压惊(领导先开会,把任务指明,通过策略获取价格)

    package com.demo.strategy;
    
    /**
     * Strategy:策略接口
     * 这个是对类NoStrategy改成面向接口的方式实现策略,不要像NoStrategy一样,
     * 直接写死策略的实现,而是使用这个接口先定义策略,功能实现后面再说
     * @author zhangxiaoxiang
     * @date 2019/5/24
     */
    public interface Strategy {
    	/**
    	 * 通过策略获取价格
    	 * @param standardPrice
    	 * @return
    	 */
    	 double getPrice(double standardPrice);
    }
    

    2:面向接口,组合编程,少用继承(继承虽然可以复用代码,但是会造成耦合度增加,解决方式往往采用接口做类的属性),如下,这样所有实现Strategy 的各种策略实现类都"组合"到这个类里面了,是不是所谓的高内聚啊

    //package com.demo.strategy;
    /**
     * Context:策略模式上下文---策略接收器,专门接收策略实现的算法
     * 负责和具体的策略类交互
     * 这样的话,具体的算法和直接的客户端调用分离了,使得算法可以独立于客户端独立的变化。
     * 如果使用spring的依赖注入功能,还可以通过配置文件,动态的注入不同策略对象,动态的切换不同的算法.
     * @author zhangxiaoxiang
     * @date: 2019/05/24
     */
    public class Context {
    	/**
    	 * 当前采用的算法对象
    	 * 面向接口,组合编程,少用继承
    	 * 简言之复杂类型(类,接口等)做属性
    	 */
    	private Strategy strategy;
    
    	/**
    	 * 选择策略Strategy实现类
    	 * 有参构造器(不写无参构造器,那么new 策略实现保证必须传一种策略,这里set方法也不用设置,
    	 * 设置了也没用(要设置set方法那么还是把无参构造也写出来才会有用,所以set伴随无参构造的感觉)
    	 * 这样同时也知道了为什么有参构造器设置了为什么无参构造器就失效了,JDK这样设计是有一定道理的,哈哈)
    	 * ---总之set注入也行,而且也推荐,也是一种组合/聚合的形式,只是这个例子采用构造器而已
    	 * @param strategy
    	 */
    	public Context(Strategy strategy) {
    		this.strategy = strategy;
    	}
    
    	public double getReultPrice(double price){
    		return this.strategy.getPrice(price);
    	}
    	//我的例子没有使用set方式注入而已,也可以使用它哈
    	// public void setStrategy(Strategy strategy) {
    	// 	this.strategy = strategy;
    	// }
    }
    
    

    3:既然是策略模式接口Strategy都明确了要做的事情是根据会员情况,返回价格,但是没有真正的实现,那么总有类来实现赛

    策略实现类1    VIP0Strategy 

    //package com.demo.strategy;
    /**
     * VIP0Strategy:普通会员策略
     *
     * @author zhangxiaoxiang
     * @date: 2019/05/24
     */
    public class VIP0Strategy implements Strategy {
    	/**
    	 * 输入一个价格,经过VIP0Strategy策略计算价格
    	 * @param standardPrice
    	 * @return
    	 */
    	@Override
    	public double getPrice(double standardPrice) {
    		System.out.println("[策略模式]普通会员 原价:"+standardPrice);
    		return standardPrice;
    	}
    
    }
    
    

     策略实现类2    VIP1Strategy 

    package com.demo.strategy;
    
    /**
     * VIP1Strategy: 一级会员策略
     *
     * @author zhangxiaoxiang
     * @date 2019/5/24
     */
    public class VIP1Strategy implements Strategy {
        /**
         * 输入一个价格,经过VIP1Strategy策略计算价格
         * @param standardPrice
         * @return
         */
        @Override
        public double getPrice(double standardPrice) {
            System.out.println("[策略模式]一级会员 打九折:"+standardPrice * 0.9);
            return standardPrice * 0.9;
        }
    
    }
    

    策略实现类3    VIP1Strategy 

    package com.demo.strategy;
    /**
     * VIP2Strategy:二级会员策略
     * @author zhangxiaoxiang
     * @date 2019/5/24
     */
    public class VIP2Strategy implements Strategy {
    	/**
    	 * 输入一个价格,经过VIP2Strategy策略计算价格
    	 * @param standardPrice
    	 * @return
    	 */
    	@Override
    	public double getPrice(double standardPrice) {
    		System.out.println("[策略模式]二级会员八折:"+standardPrice*0.8);
    		return standardPrice*0.8;
    	}
    
    }
    
    

     类似的策略实现类N ........随意拓展策略就是了

     客户端(使用者)使用----->后面加粗这段话可以忽略,也可以思考 客户端可以理解为前端传到后端的controller里面来了,我们设想,加入前端传入的需求(比如"普通客户大批量", 1000),那么仍然需要if else判断,因为这里需要选择策略,还是逃不掉选择困难症,如果已知前端的参数,那么这个策略也就知道了,就逃开if else的魔爪了,这也是策略模式的弊端(不过把所有策略通过IOC注入到一个map,然后使用get(入参),这样也是一种解决方式,不过不太优雅,反射+枚举估计是个不错的选择,老衲没有尝试的,有空再试),需要知道策略才行,不知道策略是谁,就不知道new 那个VIPNStrategy(),你仔细想一想是不是这个逻辑.至于new 策略可以使用spring 的IOC容器去做

    //package com.demo.strategy;
    
    /**
     * Client:策略模式客户端---Client 的main方法 可以想象成我们在使用别人写好的框架,我们有新的需求,对框架开发者来说就是需要对已有的
     * 代码进行维护升级,比如此时我们修改NoStrategy类,那么修改完后新版本的框架NoStrategy类很有能是对已经在使用的客户机制上不兼容的,如果
     * 用户升级为新版框架,遇到使用NoStrategy类的会报错,各种不兼容就不符合开发者维护的版本的规范,所以修改已有的类是极其不科学的
     *
     *
     * @author zhangxiaoxiang
     * @date: 2019/05/24
     */
    public class Client {
        public static void main(String[] args) {
            //System.out.println("------------------假如这里是service实现类,那么不知道使用哪种策略的就还是要根据参数new(或者spring IOC去取),感觉还是没有逃脱if else(所以结合工厂模式最佳啦,这个大伙可以思考一下,有空再更新这个博客)-----------------------");
            System.out.println("未使用模式-----------------------------------------");
            NoStrategy noStrategy=new NoStrategy();
            double price = noStrategy.getPrice("普通客户大批量", 1000);
            System.out.println(price);
            System.out.println("\n测试策略------------------------------------------");
            Context context0 = new Context(new VIP1Strategy());
            double resultPrice = context0.getReultPrice(1000);
            System.out.println(resultPrice);
    
    
            System.out.println("\n---怎么体现策略模式呢?比如现在需求是增加一种会员机制,  '老客户特大批量'  ,\n那么显然打折力度更大," +
                    "我们设置为6折,分别在未使用策略模式和使用了策略模式的基础上拓展,看那个更加易于拓展,方便维护---\n");
    
            //首先这这里,作为客户端只能够传入 "老客户特大批量" 和价格1000元,但是计算代码再服务器NoStrategy类里面,如果不去修改服务器NoStrategy
            //	那么这里是无法实现的,策略模式也是一样的,那么回到服务器端思考,不用设计模式就要修改NoStrategy里面的if else之类的代码,使用策略模式
            //    就要增加新的策略实现,其实差不太多
    
            //新增策略后未使用模式(会修该策略核心类)
            NoStrategy noStrategy1=new NoStrategy();
            double price1 = noStrategy1.getPrice("老客户特大批量", 1000);
            System.out.println(price1);
    
    
    
            //新增策略后使用模式(不会修改策略接口,只是添加一个实现)
            Context context2=new Context(new VPI4Strategy()) ;
            double price2 = context2.getReultPrice(1000);
            System.out.println(price2);
    
    
            System.out.println("\n结论:修改服务器端已经写好了的类是极其不好的维护形式,因为这个类NoStrategy" +
                    "\n可能在别的类中作为依赖或者叫做别的类引用了该类,在不明确的情况下,可能牵一发动全身,是不好的维护方式,使用了策略模式," +
                    "\n我们对只是添加了一个策略接口的实现,低侵入式,不会对已有代码造成影响,低耦合");
    
    
        }
    }
    

    使用模式的成果

    输出结果

     

    小结和抛出一些观点:上面基本已经完成策略模式的应用了,当然缺点就是调用端需要对策略有哪些熟悉,不知道的话就不能传入策略,所以这是最大的缺点.其实java的JDK8的函数式编程和Lambda表达式(简化匿名类等写法)可以让策略模式更加优雅,其实就是相当于JDK8新特性是把23中设计模式更加抽象的方式用在新语法上了,符合时代潮流,拓展java的函数式编程领域,可以大概参考哈新特性  https://zhangxiaoxiang.blog.csdn.net/article/details/100638661

    展开全文
  • 史上最全设计模式导学目录(完整版)

    万次阅读 多人点赞 2013-12-24 23:15:16
    2012年-2013年,Sunny在CSDN技术博客中陆续发表了100多篇与设计模式相关的文章,涵盖了七个面向对象设计原则和24个设计模式(23个GoF设计模式 + 简单工厂模式),为了方便大家学习,现将所有与设计模式学习相关文章...

       圣诞献礼!   

           2012年-2013年,Sunny在CSDN技术博客中陆续发表了100多篇与设计模式学习相关的文章,涵盖了七个面向对象设计原则和24个设计模式(23个GoF设计模式 +  简单工厂模式,为了方便大家学习,现将所有文章的链接进行了整理,希望能给各位带来帮助!

           祝大家圣诞节快乐! 花絮:本文的工作量大大超过之前的估计,几乎整个平安夜都花在它身上了,

     

    基础知识

     

    设计模式概述

    从招式与内功谈起——设计模式概述(一):设计模式从何而来?

    从招式与内功谈起——设计模式概述(二):设计模式是什么?

    从招式与内功谈起——设计模式概述(三):设计模式有什么用?附:个人观点

     

    面向对象设计原则

    面向对象设计原则概述

    面向对象设计原则之单一职责原则

    面向对象设计原则之开闭原则

    面向对象设计原则之里氏代换原则

    面向对象设计原则之依赖倒转原则

    面向对象设计原则之接口隔离原则

    面向对象设计原则之合成复用原则

    面向对象设计原则之迪米特法则

     

    六个创建型模式

     

    简单工厂模-Simple Factory Pattern【学习难度:★★☆☆☆,使用频率:★★★☆☆

    工厂三兄弟之简单工厂模式(一):图表库的设计

    工厂三兄弟之简单工厂模式(二):简单工厂模式概述

    工厂三兄弟之简单工厂模式(三):图表库的简单工厂模式解决方案

    工厂三兄弟之简单工厂模式(四):图表库解决方案的改进,简单工厂模式的简化,简单工厂模式总结

     

    工厂方法模式-Factory Method Pattern【学习难度:★★☆☆☆,使用频率:★★★★★

    工厂三兄弟之工厂方法模式(一):日志记录器的设计

    工厂三兄弟之工厂方法模式(二):工厂方法模式概述

    工厂三兄弟之工厂方法模式(三):日志记录器的工厂方法模式解决方案,反射与配置文件

    工厂三兄弟之工厂方法模式(四):重载的工厂方法,工厂方法的隐藏,工厂方法模式总结

     

    抽象工厂模式-Abstract  Factory Pattern【学习难度:★★★★☆,使用频率:★★★★★

    工厂三兄弟之抽象工厂模式(一):界面皮肤库的初始设计

    工厂三兄弟之抽象工厂模式(二):产品等级结构与产品族

    工厂三兄弟之抽象工厂模式(三):抽象工厂模式概述

    工厂三兄弟之抽象工厂模式(四):界面皮肤库的抽象工厂模式解决方案

    工厂三兄弟之抽象工厂模式(五):“开闭原则”的倾斜性,抽象工厂模式总结

     

    单例模式-Singleton Pattern【学习难度:★☆☆☆☆,使用频率:★★★★☆

    确保对象的唯一性——单例模式 (一):单例模式的动机,单例模式概述

    确保对象的唯一性——单例模式 (二):负载均衡器的设计与实现

    确保对象的唯一性——单例模式 (三):饿汉式单例与懒汉式单例的讨论

    确保对象的唯一性——单例模式 (四):一种更好的单例实现方法(静态内部类)

    确保对象的唯一性——单例模式 (五):单例模式总结

     

    原型模式-Prototype Pattern【学习难度:★★★☆☆,使用频率:★★★☆☆

    对象的克隆——原型模式(一):大同小异的工作周报,原型模式概述

    对象的克隆——原型模式(二):工作周报的原型模式解决方案

    对象的克隆——原型模式(三):带附件的周报【浅克隆,深克隆】

    对象的克隆——原型模式(四):原型管理器的引入和实现,原型模式总结

     

    建造者模式-Builder Pattern【学习难度:★★★★☆,使用频率:★★☆☆☆

    复杂对象的组装与创建——建造者模式(一):游戏角色设计,建造者模式概述

    复杂对象的组装与创建——建造者模式(二):游戏角色设计的建造者模式解决方案

    复杂对象的组装与创建——建造者模式(三):关于Director的进一步讨论,建造者模式总结

     

     

    七个结构型模式

     

    适配器模式-Adapter Pattern【学习难度:★★☆☆☆,使用频率:★★★★☆

     

    不兼容结构的协调——适配器模式(一):没有源码的算法库,适配器模式概述

    不兼容结构的协调——适配器模式(二):没有源码的算法库的适配器模式解决方案

    不兼容结构的协调——适配器模式(三):类适配器,双向适配器

    不兼容结构的协调——适配器模式(四):缺省适配器,适配器模式总结

     

    桥接模式-Bridge Pattern【学习难度:★★★☆☆,使用频率:★★★☆☆

    处理多维度变化——桥接模式(一):跨平台图像浏览系统

    处理多维度变化——桥接模式(二):桥接模式概述

    处理多维度变化——桥接模式(三):跨平台图像浏览系统的桥接模式解决方案

    处理多维度变化——桥接模式(四):适配器模式与桥接模式的联用,桥接模式总结

     

    组合模式-Composite Pattern【学习难度:★★★☆☆,使用频率:★★★★☆

    树形结构的处理——组合模式(一):设计杀毒软件的框架结构

    树形结构的处理——组合模式(二):组合模式概述

    树形结构的处理——组合模式(三):杀毒软件的框架结构的组合模式解决方案

    树形结构的处理——组合模式(四):透明组合模式与安全组合模式

    树形结构的处理——组合模式(五):公司组织结构,组合模式总结

     

    装饰模式-Decorator Pattern【学习难度:★★★☆☆,使用频率:★★★☆☆

    扩展系统功能——装饰模式(一):图形界面构件库的设计

    扩展系统功能——装饰模式(二):装饰模式概述

    扩展系统功能——装饰模式(三):图形界面构件库的装饰模式解决方案

    扩展系统功能——装饰模式(四):透明装饰模式与半透明装饰模式,装饰模式注意事项,装饰模式总结

     

    外观模式-Facade Pattern【学习难度:★☆☆☆☆,使用频率:★★★★★

    深入浅出外观模式(一):外观模式概述,外观模式结构与实现

    深入浅出外观模式(二):外观模式应用实例(文件加密模块)

    深入浅出外观模式(三):抽象外观类,外观模式效果与适用场景

     

    享元模式-Flyweight Pattern【学习难度:★★★★☆,使用频率:★☆☆☆☆

    实现对象的复用——享元模式(一):围棋棋子的设计,享元模式概述(上)

    实现对象的复用——享元模式(二):享元模式概述(下)

    实现对象的复用——享元模式(三):围棋棋子的享元模式解决方案

    实现对象的复用——享元模式(四):带外部状态的围棋棋子解决方案

    实现对象的复用——享元模式(五):单纯享元模式和复合享元模式,关于享元模式的几点补充,享元模式总结

     

    代理模式-Proxy Pattern【学习难度:★★★☆☆,使用频率:★★★★☆

     

    代理模式(一):代理模式概述,代理模式结构与实现

    代理模式(二):代理模式应用实例(收费商务信息查询系统)

    代理模式(三):远程代理,虚拟代理,缓冲代理

    代理模式(四):代理模式效果与适用场景

     

     

    十一个行为型模式

     

    职责链模式-Chain of Responsibility Pattern【学习难度:★★★☆☆,使用频率:★★☆☆☆

    请求的链式处理——职责链模式(一):采购单的分级审批

    请求的链式处理——职责链模式(二):职责链模式概述

    请求的链式处理——职责链模式(三):采购单分级审批的职责链模式解决方案

    请求的链式处理——职责链模式(四):纯与不纯的职责链模式,职责链模式总结

     

    命令模式-Command Pattern【学习难度:★★★☆☆,使用频率:★★★★☆

    请求发送者与接收者解耦——命令模式(一):自定义功能键,命令模式概述

    请求发送者与接收者解耦——命令模式(二):自定义功能键的命令模式解决方案

    请求发送者与接收者解耦——命令模式(三):命令队列的实现

    请求发送者与接收者解耦——命令模式(四):撤销操作的简单实现

    请求发送者与接收者解耦——命令模式(五):请求日志

    请求发送者与接收者解耦——命令模式(六):宏命令,命令模式总结

     

    解释器模式-Interpreter Pattern【学习难度:★★★★★,使用频率:★☆☆☆☆

    自定义语言的实现——解释器模式(一):机器人控制程序

    自定义语言的实现——解释器模式(二):文法规则和抽象语法树

    自定义语言的实现——解释器模式(三):解释器模式概述

    自定义语言的实现——解释器模式(四):机器人控制程序的解释器模式解决方案

    自定义语言的实现——解释器模式(五):再谈Context的作用

    自定义语言的实现——解释器模式(六):解释器模式总结

     

    迭代器模式-Iterator Pattern【学习难度:★★★☆☆,使用频率:★★★★★

    遍历聚合对象中的元素——迭代器模式(一):销售管理系统中数据的遍历

    遍历聚合对象中的元素——迭代器模式(二):迭代器模式概述

    遍历聚合对象中的元素——迭代器模式(三):销售管理系统中数据的遍历的迭代器模式解决方案

    遍历聚合对象中的元素——迭代器模式(四):使用内部类实现迭代器

    遍历聚合对象中的元素——迭代器模式(五):JDK内置迭代器的使用

    遍历聚合对象中的元素——迭代器模式(六):迭代器模式总结

     

    中介者模式-Mediator Pattern【学习难度:★★★☆☆,使用频率:★★☆☆☆

    协调多个对象之间的交互——中介者模式(一):客户信息管理窗口的初始设计

    协调多个对象之间的交互——中介者模式(二):中介者模式概述

    协调多个对象之间的交互——中介者模式(三):客户信息管理窗口的中介者模式解决方案

    协调多个对象之间的交互——中介者模式(四):中介者与同事类的扩展

    协调多个对象之间的交互——中介者模式(五):中介者模式总结

     

    备忘录模式-Memento Pattern【学习难度:★★☆☆☆,使用频率:★★☆☆☆

    撤销功能的实现——备忘录模式(一):可悔棋的中国象棋

    撤销功能的实现——备忘录模式(二):备忘录模式概述

    撤销功能的实现——备忘录模式(三):中国象棋的备忘录模式解决方案

    撤销功能的实现——备忘录模式(四):实现多次撤销

    撤销功能的实现——备忘录模式(五):再谈备忘录的封装,备忘录模式总结

     

    观察者模式-Observer Pattern【学习难度:★★★☆☆,使用频率:★★★★★

    对象间的联动——观察者模式(一):多人联机对战游戏的设计

    对象间的联动——观察者模式(二):观察者模式概述

    对象间的联动——观察者模式(三):多人联机对战游戏的观察者模式解决方案

    对象间的联动——观察者模式(四):JDK对观察者模式的支持

    对象间的联动——观察者模式(五):观察者模式与Java事件处理

    对象间的联动——观察者模式(六):观察者模式与MVC,观察者模式总结

     

    状态模式-State Pattern【学习难度:★★★☆☆,使用频率:★★★☆☆

    处理对象的多种状态及其相互转换——状态模式(一):银行系统中的账户类设计

    处理对象的多种状态及其相互转换——状态模式(二):状态模式概述

    处理对象的多种状态及其相互转换——状态模式(三):账户类的状态模式解决方案

    处理对象的多种状态及其相互转换——状态模式(四):共享状态的实现

    处理对象的多种状态及其相互转换——状态模式(五):使用环境类实现状态转换

    处理对象的多种状态及其相互转换——状态模式(六):状态模式总结

     

    策略模式-Strategy Pattern【学习难度:★☆☆☆☆,使用频率:★★★★☆

    算法的封装与切换——策略模式(一):电影票打折方案

    算法的封装与切换——策略模式(二):策略模式概述

    算法的封装与切换——策略模式(三):电影票打折方案的策略模式解决方案

    算法的封装与切换——策略模式(四):策略模式的两个典型应用,策略模式总结

     

    模板方法模式-Template Method Pattern【学习难度:★★☆☆☆,使用频率:★★★☆☆

    模板方法模式深度解析(一):模板方法模式概述,模板方法模式结构与实现

    模板方法模式深度解析(二):模板方法模式应用实例(银行利息计算模块)

    模板方法模式深度解析(三):钩子方法的使用,模板方法模式效果与适用场景

     

    访问者模式-Visitor Pattern【学习难度:★★★★☆,使用频率:★☆☆☆☆

    操作复杂对象结构——访问者模式(一):OA系统中员工数据汇总

    操作复杂对象结构——访问者模式(二):访问者模式概述

    操作复杂对象结构——访问者模式(三):OA系统中员工数据汇总的访问者模式解决方案

    操作复杂对象结构——访问者模式(四):访问者模式与组合模式联用,访问者模式总结

     

    设计模式趣味学习(复习)

     

    设计模式与足球(一):创建型模式

    设计模式与足球(二):结构型模式

    设计模式与足球(三):行为型模式(上)

    设计模式与足球(四):行为型模式(下)

     

     

    设计模式综合应用实例

     

    多人联机射击游戏

     

    多人联机射击游戏中的设计模式应用(一):抽象工厂模式,建造者模式,工厂方法模式,迭代器模式,命令模式

    多人联机射击游戏中的设计模式应用(二):观察者模式,单例模式,状态模式,适配器模式

     

     

     

    数据库同步系统

     

    设计模式综合实例分析之数据库同步系统(一):数据库同步系统概述,建造者模式,简单工厂模式

    设计模式综合实例分析之数据库同步系统(二):享元模式,单例模式,观察者模式,模板方法模式

    设计模式综合实例分析之数据库同步系统(三):策略模式,组合模式,命令模式,职责链模式

     

     

     

    友情提示:请尊重作者劳动成果,如需转载本博客文章请注明出处!谢谢合作!

     

    【作者:刘伟  http://blog.csdn.net/lovelion

    展开全文
  • 23种设计模式汇总整理

    万次阅读 多人点赞 2015-04-09 10:57:11
    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 其实还有两类:并发型模式和线程池模式。 ...
  • CentOS 进入单用户模式

    万次阅读 2021-03-30 13:51:17
    linux centos 单用户模式
  • 设计模式-JDK工厂模式

    万次阅读 2020-11-23 16:35:25
    工厂方法模式(Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法模式使一个类的实例化延迟到其子类。 二、优缺点 1、优点 (1)良好的封装性、代码结构清晰。 如一个调用者想...
  • 设计模式 | 适配器模式及典型应用

    万次阅读 多人点赞 2018-09-20 01:37:29
    适配器模式 适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。 在...
  • JAVA设计模式初探之装饰者模式

    万次阅读 多人点赞 2014-04-01 09:07:37
    这个模式花费了挺长时间,开始有点难理解,其实就是 定义:动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活。 设计初衷:通常可以使用继承来...
  • 代理软件中PAC模式和全局模式的区别

    万次阅读 多人点赞 2019-04-25 08:35:43
    PAC模式:节省流量。 全局模式:流量消耗较多。 区别二:运行速度快慢不一样。 PAC模式:国内网站依旧走本地网络,速度快,绝大部分国外网站都走代理,速度也快。。 全局模式:所有网站都走代理,访问国内网站...
  • 新标签页增加新布局,简洁模式 插件支持更多自定义扩展功能 JSON格式化工具功能优化 新标签页增加新布局,简洁模式 新标签页新增了2种布局,旧版和简洁模式。习惯旧版同学可以通过这个设置切换为旧版。 同时也可以...
  • 腾讯会议共享PPT使用演讲者模式

    万次阅读 多人点赞 2020-03-07 11:20:25
    问题来了,按常理来说,用腾讯会议的共享桌面放映PPT时,就用不到PPT双屏时候的演讲者模式了。这对于我这样看注释的念稿党来说无疑深受打击。经过一番折腾,发现了一个可以使用演讲者模式的方法。 1. 按照管理,...
  • 单例模式的五种写法

    万次阅读 多人点赞 2019-06-23 13:52:48
    设计模式(Design pattern),提供了在软件开发过程中面临的一些问题的最佳解决方案,是Java开发者必修的一门课程。主要分创建型模式、结构型模式和行为型模式。其中接下来我们要写的是单例模式,属于创建型模式。 ...
  • 设计模式(二):几种工厂模式详解

    万次阅读 2020-05-10 21:07:43
    ​ 工厂顾名思义就是创建产品,根据产品是具体产品还是具体工厂可分为简单工厂模式和工厂方法模式,根据工厂的抽象程度可分为工厂方法模式和抽象工厂模式。该模式用于封装和管理对象的创建,是一种创建型模式。 工厂...
  • C++ 设计模式

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

    万次阅读 2020-11-20 09:51:54
    模式简介 它定义了算法家族,分别封装起来,让它们间可以相互替换,此模式让算法的变化,不会影响到使用算法的用户。 模式实现 环境类(Context)用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的...
  • 原型模式是一个创建型的模式。用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。原型...
  • 在Android的root相关的文章里经常会看到关于SElinux,Android4.3以后引进...目前 SELinux 支持三种模式,分别如下: •enforcing:强制模式,代表 SELinux 运作中,且已经正确的开始限制 domain/type 了; •perm
  • Java设计模式之原型模式

    万次阅读 2019-11-10 17:29:55
    在有些系统中,存在大量相同或相似对象的创建的工作,如果用传统的构造函数来创建对象,会比较复杂且耗时耗资源,用原型模式生成对象就很高效。原型模式属于对象的创建模式通过给出一个原型对象来指明所有创建的对象...
  • 大话设计模式(一)简单工厂模式

    万次阅读 2015-04-12 16:35:37
    今天主要设计简单工厂模式即静态工厂设计模式与工厂模式即抽象模式。 其中,简单工厂模式属于类的创建型模式,又叫做静态工厂方法模式。通过专门定义一个类来负责创建 其他类的实例,被创建的实例通常都具有共同的...
  • 常用设计模式-单例模式

    万次阅读 2020-11-20 16:32:32
    模式简介 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 实际应用场景 Spring中的Bean UML类图 talk is cheap, show me the code package com.aeert.design.singleton; import lombok....
  • iPhone XS Max(xr)进入DFU模式的方法

    万次阅读 2018-10-11 09:47:24
    iPhone XS MaxiPhone xr等都一样啊(不用绿盟小编都说一遍吧,还有xs),进入DFU模式的方法如下: 1,先用把手机通过Lightning转USB传输线连电脑; 2,打开电脑的iTunes; 3,确认连接上了后,按音量-一次,再...
  • 常用设计模式-模板方法模式

    万次阅读 2020-11-23 11:19:20
    模式简介 模板方法(Template Method)模式的定义如下:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。 该...
  • ftp-主动模式(PORT)和被动模式(PASV)

    万次阅读 多人点赞 2017-06-03 12:51:03
    简介FTP协议要用到两个TCP连接, ...而主动模式与被动模式的差异主要体现在数据连结通道上命令连接当FTP客户端需要登陆到FTP服务器上的时候,服务器与客户端需要进行一系列的身份验证过程,这个过程就叫做命令
  • 设计模式之——单例模式

    万次阅读 2021-01-28 20:40:49
    设计模式: 前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定。而是一套用来提高代码可复用性、可维护性、可读性、稳健性、以及安全性的解决方案 设计模式的本质是面向对象设计原则的实际...
  • 单例模式 工厂模式 观察者模式

    千次阅读 2020-07-05 16:51:22
    单例模式 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 单例模式是一种常见的设计模式,在计算机系统中,线程池、缓存、日志对象、对话框、打印机、数据库操作、显卡的驱动程序常被...
  • 23 种设计模式详解(全23种)

    万次阅读 多人点赞 2019-06-09 00:21:59
    设计模式的分类 ...行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 A、创建...
  • 常用设计模式-原型模式

    万次阅读 2020-11-27 10:25:17
    模式简介 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象 这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在...
  • 显然设计模式往往追求开闭原则,所以往往是面向接口编程,那么万事万物就是先写接口,把需求弄出来,这里以一辆车子在陆地上跑为基础,对它进行装饰,使它可以具备更多的"功能",达到装饰的效果.这里由于代码相对简单,直接...
  • PAC模式和全局代理模式的区别与对比

    万次阅读 多人点赞 2019-05-09 10:22:38
    SS不但支持全局模式,同时也支持PAC模式(智能分流),这样情况下,既可以代理,也不会影响国内网站打开速度! PAC模式和全局模式 PAC模式 也就是智能分流模式,根据规则去匹配你访问的网站,国外网站走代...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,741,964
精华内容 696,785
关键字:

模式