设计模式 订阅
软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 展开全文
软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
信息
特    点
每个模式都有各自的名字
别    名
设计模式
分    类
基础模式,委托模式,接口模式等
中文名
软件设计模式
外文名
Design pattern
软件设计模式简介
设计模式(英语 design pattern)是对面向对象设计中反复出现的问题的解决方案。这个术语是在1990年代由Erich Gamma等人从建筑设计领域引入到计算机科学中来的。这个术语的含义还存有争议。算法不是设计模式,因为算法致力于解决问题而非设计问题。设计模式通常描述了一组相互紧密作用的类与对象。设计模式提供一种讨论软件设计的公共语言,使得熟练设计者的设计经验可以被初学者和其他设计者掌握。设计模式还为软件重构提供了目标。随着软件开发社群对设计模式的兴趣日益增长,已经出版了一些相关的专著,定期召开相应的研讨会,而且Ward Cunningham为此发明了WikiWiki用来交流设计模式的经验。
收起全文
精华内容
参与话题
问答
  • 常用设计模式总结

    万次阅读 多人点赞 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设计模式之单例模式

    万次阅读 多人点赞 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-22 09:33:22
    设计模式简介 设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过...

    设计模式简介

    设计模式(Design Patterns)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。

    什么是 GOF?(四人帮,全拼 Gang of Four)?

    在 1994 年,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著出版了一本名为 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 的书,该书首次提到了软件开发中设计模式的概念。

    四位作者合称 GOF(四人帮,全拼 Gang of Four)。他们所提出的设计模式主要是基于以下的面向对象设计原则。

    • 对接口编程而不是对实现编程。
    • 优先使用对象组合而不是继承。

    设计模式的使用

    设计模式在软件开发中的两个主要用途。

    开发人员的共同平台

    设计模式提供了一个标准的术语系统,且具体到特定的情景。例如,单例设计模式意味着使用单个对象,这样所有熟悉单例设计模式的开发人员都能使用单个对象,并且可以通过这种方式告诉对方,程序使用的是单例模式。

    最佳的实践

    设计模式已经经历了很长一段时间的发展,它们提供了软件开发过程中面临的一般问题的最佳解决方案。学习这些模式有助于经验不足的开发人员通过一种简单快捷的方式来学习软件设计。

    设计模式的类型

    根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)。当然,我们还会讨论另一类设计模式:J2EE 设计模式。

    GitHub源码链接

    序号 模式 & 描述 包括
    1 创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern)
    2 结构型模式 这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。 适配器模式(Adapter Pattern) 桥接模式(Bridge Pattern) 过滤器模式(Filter、Criteria Pattern) 组合模式(Composite Pattern) 装饰器模式(Decorator Pattern) 外观模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern)
    3 行为型模式 这些设计模式特别关注对象之间的通信。 责任链模式(Chain of Responsibility Pattern) 命令模式(Command Pattern) 解释器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 备忘录模式(Memento Pattern) 观察者模式(Observer Pattern) 状态模式(State Pattern) 空对象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 访问者模式(Visitor Pattern)
    4 J2EE 模式 这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。 MVC 模式(MVC Pattern) 业务代表模式(Business Delegate Pattern) 组合实体模式(Composite Entity Pattern) 数据访问对象模式(Data Access Object Pattern) 前端控制器模式(Front Controller Pattern) 拦截过滤器模式(Intercepting Filter Pattern) 服务定位器模式(Service Locator Pattern) 传输对象模式(Transfer Object Pattern)

    下面用一个图片来整体描述一下设计模式之间的关系:

    设计模式之间的关系

    设计模式的六大原则

    1、开闭原则(Open Close Principle)

    开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

    2、里氏代换原则(Liskov Substitution Principle)

    里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

    3、依赖倒转原则(Dependence Inversion Principle)

    这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

    4、接口隔离原则(Interface Segregation Principle)

    这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

    5、迪米特法则,又称最少知道原则(Demeter Principle)

    最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。

    6、合成复用原则(Composite Reuse Principle)

    合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。

    创建型模式

    抽象工厂模式

    单例模式

    工厂模式

    建造者模式

    原型模式

    结构型模式

    代理模式

    过滤器模式

    桥接模式

    适配器模式

    外观模式

    享元模式

    装饰器模式

    组合模式

    行为型模式

    备忘录模式

    策略模式

    迭代器模式

    访问者模式

    观察者模式

    解释器模式

    空对象模式

    命令模式

    模板模式

    责任链模式

    中介者模式

    状态模式

    J2EE模式

    MVC模式

    传输对象模式

    服务定位器模式

    拦截过滤器模式

    前端控制器模式

    数据访问对象模式

    业务代表模式

    组合实体模式

    展开全文
  • 设计模式-原型模式

    万次阅读 2019-12-07 22:36:42
    原型模式是一个创建型的模式。原型二字表明了改模式应该有一个样板实例,用户从这个样板对象中复制一个内部属性一致的对象,这个过程也就是我们称的“克隆”。被复制的实例就是我们所称的“原型”,这个...

     

    目录

     

    知识点1:什么是原型模式

    知识点2:原型模式应用场景

    知识点3:原型模式UML类图(通用)

    知识点4:实例

    知识点5:原型模式分为浅复制和深复制


    知识点1:什么是原型模式

    克隆

    原型模式是一个创建型的模式。原型二字表明了改模式应该有一个样板实例,用户从这个样板对象中复制一个内部属性一致的对象,这个过程也就是我们称的“克隆”。被复制的实例就是我们所称的“原型”,这个原型是可定制的。原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。


    知识点2:原型模式应用场景

    (1)类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。 
    (2)通过new产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式。 
    (3)一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。

    Spring框架中的多例就是使用原型。


    知识点3:原型模式UML类图(通用)

    原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype。Prototype类需要具备以下两个条件: 
      (1)实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。 
      (2)重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此Prototype类需要将clone方法的作用域修改为public类型。


    知识点4:实例

    /*
     * 书本类型,扮演的是ConcretePrototype角色,而Cloneable扮演Prototype角色
     */
    public class Book implements Cloneable {
    
    	private String title;// 标题
    	private ArrayList<String> image = new ArrayList<String>();// 图片名列表
    
    	public Book() {
    		super();
    	}
    
    	/**
    	 * 重写拷贝方法
    	 */
    	@Override
    	protected Book clone() {
    		try {
    			Book book = (Book) super.clone();//
    			book.image=(ArrayList<String>)this.image.clone();//深复制
    			return book;
    		} catch (CloneNotSupportedException e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    
    	public ArrayList<String> getImage() {
    		return image;
    	}
    
    	public void addImage(String img) {
    		this.image.add(img);
    	}
    
    	public String getTitle() {
    		return title;
    	}
    
    	public void setTitle(String title) {
    		this.title = title;
    	}
    
    	/**
    	 * 打印内容
    	 */
    	public void showBook() {
    		System.out.println("----------------------Start----------------------");
    
    		System.out.println("title:" + title);
    		for (String img : image) {
    			System.out.println("image name:" + img);
    		}
    
    		System.out.println("----------------------End----------------------");
    	}
    }
    
    
    客户端代码
    public class Client02 {
    
    	public static void main(String[] args) {
    		Book book1 = new Book();
    		book1.setTitle("书1");
    		book1.addImage("图1");
    		book1.showBook();
    		//以原型方式拷貝一份
    		Book book2 = book1.clone();
    		book2.showBook();
    		book2.setTitle("书2");
    		book2.addImage("圖2");
    		book2.showBook();
    		//再次还原打印书本
    		book1.showBook();
    		}
    
    }

    知识点5:原型模式分为浅复制和深复制

    浅复制 —-只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做“(浅复制)浅拷贝”,换句话说,浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变。

    深复制 —-在计算机中开辟了一块新的内存地址用于存放复制的对象。


     

     

    展开全文
  • 设计模式-装饰模式

    万次阅读 2019-12-07 22:41:22
     装饰器模式,也成为包装模式,顾名思义,就是对已经存在的某些类进行装饰,以此来扩展一些功能。其结构图如下: Component为统一接口,也是装饰类和被装饰类的基本类型。 ConcreteComponent为具体实现类,也是...
  • 设计模式-外观模式

    万次阅读 2019-12-03 22:56:22
    目录 ...这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。 这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。 ...
  • 设计模式-适配器模式

    万次阅读 2019-12-03 22:34:55
    目录 知识点1:什么是适配器 知识点2:适配器分类 知识点3:适配器案例 1、定义日本和中国两种接口及其实现 ...在设计模式中,适配器模式(英语:adapter pattern)有时候也称包装样式或者包...
  • 设计模式-工厂模式

    万次阅读 2019-12-02 22:40:03
    知识点1:什么是工厂模式 知识点2:工厂模式好处 知识点3:工厂模式分类 1、简单工厂模式 (1)单工厂的优点/缺点 2、工厂方法模式 (1)什么是工厂方法模式 3、抽象工厂模式 (1)什么是抽象工厂模式 知识...
  • 设计模式-代理模式

    万次阅读 2019-12-03 07:50:03
    知识点1:什么是代理模式 知识点2:代理模式应用场景 知识点3:代理的分类 知识点4:静态代理 1、什么是静态代理 2、静态代理代码 知识点5:动态代理 1、什么是动态代理 2、JDK动态代理 3、CGLIB动态代理 ...
  • 设计模式-单例模式

    万次阅读 2019-12-02 22:26:53
    知识点1:设计模式分类 知识点2:设计模式的原则 1、开闭原则(Open Close Principle) 2、里氏代换原则(Liskov Substitution Principle) 3、依赖倒转原则(Dependence Inversion Principle) 4、迪米特法则...
  • 设计模式-观察者模式

    万次阅读 2019-12-08 09:50:12
    观察者模式(Observer),是一种行为性模型,行为型模式关注的是系统中对象之间的相互交互,解决系统在运行时对象之间的相互通信和协作,进一步明确对象的职责。相比来说,创建型模式关注对象的创建过程,结构型模式...
  • 设计模式-建造者模式

    万次阅读 2019-12-03 21:06:56
    目录 知识点1:什么是建造者模式 知识点2:建造者应用场景 知识点3:实际案例 1、建立一个人物对象Person 2、建立Builder接口 ...3、创建实现类:ConcreteBuilder...工厂类模式提供的是创建单个类的模式,而建造者...
  • 在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的。但是在一些情况下, new操作符直接生成对象会带来一些问题。举例来说, 许多类型对象的创造需要一系列的步骤: 你...
  • JAVA设计模式

    千次阅读 多人点赞 2017-12-21 21:40:49
    JAVA设计模式
  • C++设计模式

    千次阅读 多人点赞 2019-03-11 14:49:19
    C++设计模式——简单工厂模式 C++设计模式——工厂方法模式 C++设计模式——抽象工厂模式 C++设计模式——单例模式 C++设计模式——建造者模式 C++设计模式——原型模式 C++设计模式——适配器模式 C++设计模式——...
  • 设计模式-模版方法

    万次阅读 2019-12-03 21:18:48
    目录 知识点1:什么是模板方法 ...模板方法模式:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的 重复代码全部在父类里面,不同业务的,...
  • 23种设计模式汇总整理

    万次阅读 多人点赞 2015-04-09 10:57:11
    设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式 结构型模式,共七种:适配器模式、装饰者模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 ...
  • MVC设计模式和MVC框架的区别

    千次阅读 多人点赞 2017-11-20 09:34:14
    MVC框架和MVC设计模式的区别
  • 图解Java设计模式(一)

    万人学习 2019-02-27 21:06:35
    本课程针对上述问题,有针对性的进行了升级 (1) 授课方式采用 图解+框架源码分析的方式,让课程生动有趣好理解 (2) 系统全面的讲解了设计模式,包括 设计模式七大原则、UML类图-类的六大关系、23种设计模式及其分类,...
  • 史上最全设计模式导学目录(完整版)

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

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

空空如也

1 2 3 4 5 ... 20
收藏数 403,095
精华内容 161,238
关键字:

设计模式