精华内容
下载资源
问答
  • Java中常用设计模式

    万次阅读 多人点赞 2019-03-15 17:25:14
    一、什么是设计模式 设计模式(Design pattern)是一套被反复使用、多数人知晓的、... 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式软件工程的基石,如同大厦的一块...

    文章转载借鉴:http://blog.csdn.net/zhangerqing

    一、什么是设计模式

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

    二、设计模式的六大原则

    1、开闭原则(Open Close Principle)

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

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

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

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

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

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

    这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

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

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

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

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

    设计模式原则详细描述请见:http://www.cnblogs.com/pony1223/p/7594803.html

    三、设计模式的三大类

    总体来说设计模式分为三大类:

    创建型模式(5种):工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

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

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

     其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:

    根据作用范围来分

    根据模式是主要用于类上还是主要用于对象上来分,这种方式可分为类模式和对象模式两种。

    1. 类模式:用于处理类与子类之间的关系,这些关系通过继承来建立,是静态的,在编译时刻便确定下来了。工厂方法、(类)适配器、模板方法、解释器属于该模式。
    2. 对象模式:用于处理对象之间的关系,这些关系可以通过组合或聚合来实现,在运行时刻是可以变化的,更具动态性。
    范围\目的创建型模式结构型模式行为型模式
    类模式工厂方法(类)适配器模板方法、解释器
    对象模式单例
    原型
    抽象工厂
    建造者
    代理
    (对象)适配器
    桥接
    装饰
    外观
    享元
    组合
    策略
    命令
    职责链
    状态
    观察者
    中介者
    迭代器
    访问者
    备忘录

    四、Java的二十三种设计模式

    从这一块开始,我们详细介绍Java中23种设计模式的概念,应用场景等情况,并结合他们的特点及设计模式的原则进行分析。

    创建型模式(5种):用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。

    A、单例模式(Singleton)

    单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。

    这样的模式有几个好处:

    1. 某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
    2. 省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
    3. 有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。

    优点:只有一个实例,节约了内存资源,提高了系统性能

    缺点:
        没有抽象层,不能扩展
        职责过重,违背了单一性原则

    首先我们写一个简单的单例类:

    public class Singleton {
     
    	/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
    	private static Singleton instance = null;
     
    	/* 私有构造方法,防止被实例化 */
    	private Singleton() {
    	}
     
    	/* 静态工程方法,创建实例 */
    	public static Singleton getInstance() {
    		if (instance == null) {
    			instance = new Singleton();
    		}
    		return instance;
    	}
     
    	/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
    	public Object readResolve() {
    		return instance;
    	}
    }

    这个类可以满足基本要求,但是,像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,肯定就会出现问题了,如何解决?我们首先会想到对getInstance方法加synchronized关键字,如下:

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

    但是,synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了,所以,这个地方需要改进。我们改成下面这个:

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

    似乎解决了之前提到的问题,将synchronized关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在instance为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能有问题的,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:

    ①:A、B线程同时进入了第一个if判断

    ②:A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();

    ③:由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。

    ④:B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。

    ⑥:此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。

    所以程序还是有可能发生错误,其实程序在运行过程是很复杂的,从这点我们就可以看出,尤其是在写多线程环境下的程序更有难度,有挑战性。我们对该程序做进一步优化:

    private static class SingletonFactory{         
            private static Singleton instance = new Singleton();         
        }         
    public static Singleton getInstance(){         
            return SingletonFactory.instance;         
        } 

     实际情况是,单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。这样我们暂时总结一个完美的单例模式:

    public class Singleton {
     
    	/* 私有构造方法,防止被实例化 */
    	private Singleton() {
    	}
     
    	/* 此处使用一个内部类来维护单例 */
    	private static class SingletonFactory {
    		private static Singleton instance = new Singleton();
    	}
     
    	/* 获取实例 */
    	public static Singleton getInstance() {
    		return SingletonFactory.instance;
    	}
     
    	/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
    	public Object readResolve() {
    		return getInstance();
    	}
    }

    其实说它完美,也不一定,如果在构造函数中抛出异常,实例将永远得不到创建,也会出错。所以说,十分完美的东西是没有的,我们只能根据实际情况,选择最适合自己应用场景的实现方法。也有人这样实现:因为我们只需要在创建类的时候进行同步,所以只要将创建和getInstance()分开,单独为创建加synchronized关键字,也是可以的:

    public class SingletonTest {
     
    	private static SingletonTest instance = null;
     
    	private SingletonTest() {
    	}
     
    	private static synchronized void syncInit() {
    		if (instance == null) {
    			instance = new SingletonTest();
    		}
    	}
     
    	public static SingletonTest getInstance() {
    		if (instance == null) {
    			syncInit();
    		}
    		return instance;
    	}
    }

    考虑性能的话,整个程序只需创建一次实例,所以性能也不会有什么影响。

    public class SingletonTest {
     
    	private static SingletonTest instance = null;
    	private Vector properties = null;
     
    	public Vector getProperties() {
    		return properties;
    	}
     
    	private SingletonTest() {
    	}
     
    	private static synchronized void syncInit() {
    		if (instance == null) {
    			instance = new SingletonTest();
    		}
    	}
     
    	public static SingletonTest getInstance() {
    		if (instance == null) {
    			syncInit();
    		}
    		return instance;
    	}
     
    	public void updateProperties() {
    		SingletonTest shadow = new SingletonTest();
    		properties = shadow.getProperties();
    	}
    }

     通过单例模式的学习告诉我们:

    1. 单例模式理解起来简单,但是具体实现起来还是有一定的难度。
    2. synchronized关键字锁定的是对象,在用的时候,一定要在恰当的地方使用(注意需要使用锁的对象和过程,可能有的时候并不是整个对象及整个过程都需要锁)。

    到这儿,单例模式基本已经讲完了,结尾处,笔者突然想到另一个问题,就是采用类的静态方法,实现单例模式的效果,也是可行的,此处二者有什么不同?

    首先,静态类不能实现接口。(从类的角度说是可以的,但是那样就破坏了静态了。因为接口中不允许有static修饰的方法,所以即使实现了也是非静态的)

    其次,单例可以被延迟初始化,静态类一般在第一次加载是初始化。之所以延迟加载,是因为有些类比较庞大,所以延迟加载有助于提升性能。

    再次,单例类可以被继承,他的方法可以被覆写。但是静态类内部方法都是static,无法被覆写。

    最后一点,单例类比较灵活,毕竟从实现上只是一个普通的Java类,只要满足单例的基本需求,你可以在里面随心所欲的实现一些其它功能,但是静态类不行。从上面这些概括中,基本可以看出二者的区别,但是,从另一方面讲,我们上面最后实现的那个单例模式,内部就是用一个静态类来实现的,所以,二者有很大的关联,只是我们考虑问题的层面不同罢了。两种思想的结合,才能造就出完美的解决方案,就像HashMap采用数组+链表来实现一样,其实生活中很多事情都是这样,单用不同的方法来处理问题,总是有优点也有缺点,最完美的方法是,结合各个方法的优点,才能最好的解决问题!

    拓展:多例设计模式

    单例设计模式只留下一个类的一个实例化对象,而多例设计模式,会定义出多个对象。例如:定义一个表示星期的操作类,这个类的对象只能有7个实例化对象(星期一 ~ 星期日);定义一个表示性别的类,只能有2个实例化对象(男、女);定义一个表示颜色的操作类,只能有3个实例化对象(红、绿、蓝)。这种情况下,这样的类就不应该由用户无限制地去创造实例化对象,应该只使用有限的几个,这个就属于多例设计模式。不管是单例设计模式还是多例设计模式,有一个核心不可动摇,即构造器方法私有化。

    class Sex{
        private String title;
        private static final Sex MALE = new Sex("男");
        private static final Sex FEMALE = new Sex("女");
    
        private Sex(String title){        //构造器私有化
            this.title = title;
        }
    
        public String toString(){
            return this.title;
        }
        
        public static Sex getInstance(int ch){
            switch(ch){
                case 1:
                    return MALE;
                case 2:
                    return FEMALE;
                default:
                    return null;
            }
        }
    }
    
    public class TestDemo{
        public static void main(String args[]){
            Sex sex = Sex.getInstance(2);
            System.out.println(sex);
        }
    }
    
    
    ==========程序执行结果=========
    女

    B、工厂方法模式(Factory Method)

    工厂方法模式分为三种:

    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、多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。关系图:

    将上面的代码做下修改,改动下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、静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

    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!

    总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。

    好处:客户端不需要创建对象,明确了各个类的职责
    缺点:该工厂类负责创建所有实例,如果有新的类加入,需要不断的修改工厂类,不利于后期的维护

    C、抽象工厂模式

    工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。因为抽象工厂不太好理解,我们先看看图,然后就和代码,就比较容易理解。

    请看例子:

    public interface Sender {
    	public void Send();
    }

    两个实现类:

    public class MailSender implements Sender {
    	@Override
    	public void Send() {
    		System.out.println("this is mailsender!");
    	}
    }
    public class MailSender implements Sender {
    	@Override
    	public void Send() {
    		System.out.println("this is mailsender!");
    	}
    }

    在提供一个接口:

    public interface Provider {
    	public Sender produce();
    }

    两个工厂类:

        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 class Test {
     
    	public static void main(String[] args) {
    		Provider provider = new SendMailFactory();
    		Sender sender = provider.produce();
    		sender.Send();
    	}
    }

    其实这个模式的好处就是,如果你现在想增加一个功能:发及时信息,则只需做一个实现类,实现Sender接口,同时做一个工厂类,实现Provider接口,就OK了,无需去改动现成的代码。这样做,拓展性较好!

    好处:如果有新的类进来,只需要添加一个对应的具体工厂类,不影响现有代码,增加了程序的扩展性
    缺点:增加了代码量

    D、建造者模式(Builder)

    工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。我们看一下代码:

    还和前面一样,一个Sender接口,两个实现类MailSender和SmsSender。最后,建造者类如下:

    public class Builder {
    	
    	private List<Sender> list = new ArrayList<Sender>();
    	
    	public void produceMailSender(int count){
    		for(int i=0; i<count; i++){
    			list.add(new MailSender());
    		}
    	}
    	
    	public void produceSmsSender(int count){
    		for(int i=0; i<count; i++){
    			list.add(new SmsSender());
    		}
    	}
    }

     测试类:

    public class Test {
     
    	public static void main(String[] args) {
    		Builder builder = new Builder();
    		builder.produceMailSender(10);
    	}
    }

    从这点看出,建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工程模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象,多个部分。因此,是选择工厂模式还是建造者模式,依实际情况而定。

    E、原型模式(Prototype)

    原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。本小结会通过对象的复制,进行讲解。在Java中,复制对象是通过clone()实现的,先创建一个原型类:

    public class Prototype implements Cloneable {
     
    	public Object clone() throws CloneNotSupportedException {
    		Prototype proto = (Prototype) super.clone();
    		return proto;
    	}
    }

    很简单,一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的,具体怎么实现,我会在另一篇文章中,关于解读Java中本地方法的调用,此处不再深究。在这儿,我将结合对象的浅复制和深复制来说一下,首先需要了解对象深、浅复制的概念:

    浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。

    深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。

    此处,写一个深浅复制的例子:

    public class Prototype implements Cloneable, Serializable {
     
    	private static final long serialVersionUID = 1L;
    	private String string;
     
    	private SerializableObject obj;
     
    	/* 浅复制 */
    	public Object clone() throws CloneNotSupportedException {
    		Prototype proto = (Prototype) super.clone();
    		return proto;
    	}
     
    	/* 深复制 */
    	public Object deepClone() throws IOException, ClassNotFoundException {
     
    		/* 写入当前对象的二进制流 */
    		ByteArrayOutputStream bos = new ByteArrayOutputStream();
    		ObjectOutputStream oos = new ObjectOutputStream(bos);
    		oos.writeObject(this);
     
    		/* 读出二进制流产生的新对象 */
    		ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    		ObjectInputStream ois = new ObjectInputStream(bis);
    		return ois.readObject();
    	}
     
    	public String getString() {
    		return string;
    	}
     
    	public void setString(String string) {
    		this.string = string;
    	}
     
    	public SerializableObject getObj() {
    		return obj;
    	}
     
    	public void setObj(SerializableObject obj) {
    		this.obj = obj;
    	}
     
    }
     
    class SerializableObject implements Serializable {
    	private static final long serialVersionUID = 1L;
    }

    要实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。

    结构型模式(7种):用于描述如何将类或对象按某种布局组成更多的结构。

    F、适配器模式(Adapter)

    适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。首先,我们来看看类的适配器模式,先看类图:

    核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口时Targetable,通过Adapter类,将Source的功能扩展到Targetable里,看代码:

    public class Source {
     
    	public void method1() {
    		System.out.println("this is original method!");
    	}
    }
    public interface Targetable {
     
    	/* 与原类中的方法相同 */
    	public void method1();
     
    	/* 新类的方法 */
    	public void method2();
    }
    public class Adapter extends Source implements Targetable {
     
    	@Override
    	public void method2() {
    		System.out.println("this is the targetable method!");
    	}
    }
    public class Adapter extends Source implements Targetable {
     
    	@Override
    	public void method2() {
    		System.out.println("this is the targetable method!");
    	}
    }

    Adapter类继承Source类,实现Targetable接口,下面是测试类:

    public class AdapterTest {
     
    	public static void main(String[] args) {
    		Targetable target = new Adapter();
    		target.method1();
    		target.method2();
    	}
    }

    输出:

    this is original method!
    this is the targetable method!

    这样Targetable接口的实现类就具有了Source类的功能。

    对象的适配器模式

    基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。看图:

    只需要修改Adapter类的源码即可:

    public class Wrapper implements Targetable {
     
    	private Source source;
    	
    	public Wrapper(Source source){
    		super();
    		this.source = source;
    	}
    	@Override
    	public void method2() {
    		System.out.println("this is the targetable method!");
    	}
     
    	@Override
    	public void method1() {
    		source.method1();
    	}
    }

    测试类:

    public class AdapterTest {
     
    	public static void main(String[] args) {
    		Source source = new Source();
    		Targetable target = new Wrapper(source);
    		target.method1();
    		target.method2();
    	}
    }

    输出与第一种一样,只是适配的方法不同而已。

    第三种适配器模式是接口的适配器模式,接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。看一下类图:
     

    这个很好理解,在实际开发中,我们也常会遇到这种接口中定义了太多的方法,以致于有时我们在一些实现类中并不是都需要。看代码:

    public interface Sourceable {
    	
    	public void method1();
    	public void method2();
    }

    抽象类Wrapper2:

    public abstract class Wrapper2 implements Sourceable{
    	
    	public void method1(){}
    	public void method2(){}
    }
    public class SourceSub1 extends Wrapper2 {
    	public void method1(){
    		System.out.println("the sourceable interface's first Sub1!");
    	}
    }
    public class SourceSub2 extends Wrapper2 {
    	public void method2(){
    		System.out.println("the sourceable interface's second Sub2!");
    	}
    }
    public class WrapperTest {
     
    	public static void main(String[] args) {
    		Sourceable source1 = new SourceSub1();
    		Sourceable source2 = new SourceSub2();
    		
    		source1.method1();
    		source1.method2();
    		source2.method1();
    		source2.method2();
    	}
    }

    测试输出:

    the sourceable interface's first Sub1!
    the sourceable interface's second Sub2!

    达到了我们的效果!

     讲了这么多,总结一下三种适配器模式的应用场景:

    类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。

    对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。

    接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

    G、装饰模式

    顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口。装饰对象持有被装饰对象的实例,关系图如下:

    Source类是被装饰类,Decorator类是一个装饰类,可以为Source类动态的添加一些功能,代码如下:

    public interface Sourceable {
    	public void method();
    }
    public class Source implements Sourceable {
     
    	@Override
    	public void method() {
    		System.out.println("the original method!");
    	}
    }
    public class Decorator implements Sourceable {
     
    	private Sourceable source;
    	
    	public Decorator(Sourceable source){
    		super();
    		this.source = source;
    	}
    	@Override
    	public void method() {
    		System.out.println("before decorator!");
    		source.method();
    		System.out.println("after decorator!");
    	}
    }

    测试类:

    public class DecoratorTest {
     
    	public static void main(String[] args) {
    		Sourceable source = new Source();
    		Sourceable obj = new Decorator(source);
    		obj.method();
    	}
    }

    输出:

    before decorator!
    the original method!
    after decorator!

    装饰器模式的应用场景:

    1. 需要扩展一个类的功能。
    2. 动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)

    缺点:产生过多相似的对象,不易排错!

    H、代理者模式(Proxy)

    代理(Proxy)模式:为某对象提供一种代理以

    控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。比如我们在租房子的时候会去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。先来看看关系图:

    根据上文的阐述,代理模式就比较容易的理解了,我们看下代码:

    public interface Sourceable {
    	public void method();
    }
    public class Source implements Sourceable {
     
    	@Override
    	public void method() {
    		System.out.println("the original method!");
    	}
    }
    public class Proxy implements Sourceable {
     
    	private Source source;
    	public Proxy(){
    		super();
    		this.source = new Source();
    	}
    	@Override
    	public void method() {
    		before();
    		source.method();
    		atfer();
    	}
    	private void atfer() {
    		System.out.println("after proxy!");
    	}
    	private void before() {
    		System.out.println("before proxy!");
    	}
    }

    测试类:

    public class ProxyTest {
     
    	public static void main(String[] args) {
    		Sourceable source = new Proxy();
    		source.method();
    	}
     
    }

    输出:

    before proxy!
    the original method!
    after proxy!

    代理模式的应用场景:

    如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:

    1. 修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
    2. 就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。

    使用代理模式,可以将功能划分的更加清晰,有助于后期维护!

    I、外观模式(Facade)

    外观模式是为了解决类与类之家的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该模式中没有涉及到接口,看下类图:(我们以一个计算机的启动过程为例)

    我们先看下实现类:

    public class CPU {
    	
    	public void startup(){
    		System.out.println("cpu startup!");
    	}
    	
    	public void shutdown(){
    		System.out.println("cpu shutdown!");
    	}
    }
    public class Memory {
    	
    	public void startup(){
    		System.out.println("memory startup!");
    	}
    	
    	public void shutdown(){
    		System.out.println("memory shutdown!");
    	}
    }
    public class Disk {
    	
    	public void startup(){
    		System.out.println("disk startup!");
    	}
    	
    	public void shutdown(){
    		System.out.println("disk shutdown!");
    	}
    }
    public class Computer {
    	private CPU cpu;
    	private Memory memory;
    	private Disk disk;
    	
    	public Computer(){
    		cpu = new CPU();
    		memory = new Memory();
    		disk = new Disk();
    	}
    	
    	public void startup(){
    		System.out.println("start the computer!");
    		cpu.startup();
    		memory.startup();
    		disk.startup();
    		System.out.println("start computer finished!");
    	}
    	
    	public void shutdown(){
    		System.out.println("begin to close the computer!");
    		cpu.shutdown();
    		memory.shutdown();
    		disk.shutdown();
    		System.out.println("computer closed!");
    	}
    }

    User类如下:

    public class User {
     
    	public static void main(String[] args) {
    		Computer computer = new Computer();
    		computer.startup();
    		computer.shutdown();
    	}
    }

    输出:

    start the computer!
    cpu startup!
    memory startup!
    disk startup!
    start computer finished!
    begin to close the computer!
    cpu shutdown!
    memory shutdown!
    disk shutdown!
    computer closed!

    如果我们没有Computer类,那么,CPU、Memory、Disk他们之间将会相互持有实例,产生关系,这样会造成严重的依赖,修改一个类,可能会带来其他类的修改,这不是我们想要看到的,有了Computer类,他们之间的关系被放在了Computer类里,这样就起到了解耦的作用,这就是外观模式!

    J、桥接模式

    桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。我们来看看关系图:

    实现代码:

    先定义接口:

    public interface Sourceable {
    	public void method();
    }

    分别定义两个实现类:

    public class SourceSub1 implements Sourceable {
     
    	@Override
    	public void method() {
    		System.out.println("this is the first sub!");
    	}
    }
    public class SourceSub2 implements Sourceable {
     
    	@Override
    	public void method() {
    		System.out.println("this is the second sub!");
    	}
    }

     定义一个桥,持有Sourceable的一个实例:

    public abstract class Bridge {
    	private Sourceable source;
     
    	public void method(){
    		source.method();
    	}
    	
    	public Sourceable getSource() {
    		return source;
    	}
     
    	public void setSource(Sourceable source) {
    		this.source = source;
    	}
    }
    public class MyBridge extends Bridge {
    	public void method(){
    		getSource().method();
    	}
    }

     测试类:

    public class BridgeTest {
    	
    	public static void main(String[] args) {
    		
    		Bridge bridge = new MyBridge();
    		
    		/*调用第一个对象*/
    		Sourceable source1 = new SourceSub1();
    		bridge.setSource(source1);
    		bridge.method();
    		
    		/*调用第二个对象*/
    		Sourceable source2 = new SourceSub2();
    		bridge.setSource(source2);
    		bridge.method();
    	}
    }

    output:

    this is the first sub!
    this is the second sub!

    这样,就通过对Bridge类的调用,实现了对接口Sourceable的实现类SourceSub1和SourceSub2的调用。接下来我再画个图,大家就应该明白了,因为这个图是我们JDBC连接的原理,有数据库学习基础的,一结合就都懂了。
     

    K、组合模式(Composite)

    组合模式有时又叫部分-整体模式在处理类似树形结构的问题时比较方便,看看关系图:

     直接来看代码:

    public class TreeNode {
    	
    	private String name;
    	private TreeNode parent;
    	private Vector<TreeNode> children = new Vector<TreeNode>();
    	
    	public TreeNode(String name){
    		this.name = name;
    	}
     
    	public String getName() {
    		return name;
    	}
     
    	public void setName(String name) {
    		this.name = name;
    	}
     
    	public TreeNode getParent() {
    		return parent;
    	}
     
    	public void setParent(TreeNode parent) {
    		this.parent = parent;
    	}
    	
    	//添加孩子节点
    	public void add(TreeNode node){
    		children.add(node);
    	}
    	
    	//删除孩子节点
    	public void remove(TreeNode node){
    		children.remove(node);
    	}
    	
    	//取得孩子节点
    	public Enumeration<TreeNode> getChildren(){
    		return children.elements();
    	}
    }
    public class Tree {
     
    	TreeNode root = null;
     
    	public Tree(String name) {
    		root = new TreeNode(name);
    	}
     
    	public static void main(String[] args) {
    		Tree tree = new Tree("A");
    		TreeNode nodeB = new TreeNode("B");
    		TreeNode nodeC = new TreeNode("C");
    		
    		nodeB.add(nodeC);
    		tree.root.add(nodeB);
    		System.out.println("build the tree finished!");
    	}
    }

    使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。

    L、享元模式(Flyweight)

    享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

    FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象,FlyWeight是超类。一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些个对象,他们有一些共有的属性,就拿数据库连接池来说,url、driverClassName、username、password及dbname,这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。

    看个例子:

    看下数据库连接池的代码:

    public class ConnectionPool {
    	
    	private Vector<Connection> pool;
    	
    	/*公有属性*/
    	private String url = "jdbc:mysql://localhost:3306/test";
    	private String username = "root";
    	private String password = "root";
    	private String driverClassName = "com.mysql.jdbc.Driver";
     
    	private int poolSize = 100;
    	private static ConnectionPool instance = null;
    	Connection conn = null;
     
    	/*构造方法,做一些初始化工作*/
    	private ConnectionPool() {
    		pool = new Vector<Connection>(poolSize);
     
    		for (int i = 0; i < poolSize; i++) {
    			try {
    				Class.forName(driverClassName);
    				conn = DriverManager.getConnection(url, username, password);
    				pool.add(conn);
    			} catch (ClassNotFoundException e) {
    				e.printStackTrace();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    	}
     
    	/* 返回连接到连接池 */
    	public synchronized void release() {
    		pool.add(conn);
    	}
     
    	/* 返回连接池中的一个数据库连接 */
    	public synchronized Connection getConnection() {
    		if (pool.size() > 0) {
    			Connection conn = pool.get(0);
    			pool.remove(conn);
    			return conn;
    		} else {
    			return null;
    		}
    	}
    }

    通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能! 

    行为型模式(11种):用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。

    M、策略模式(strategy)

    策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数,关系图如下:

     图中ICalculator提供统一的方法,AbstractCalculator是辅助类,提供辅助方法,接下来,依次实现下每个类:

    首先统一接口:

    public interface ICalculator {
    	public int calculate(String exp);
    }

     辅助类:

    public abstract class AbstractCalculator {
    	
    	public int[] split(String exp,String opt){
    		String array[] = exp.split(opt);
    		int arrayInt[] = new int[2];
    		arrayInt[0] = Integer.parseInt(array[0]);
    		arrayInt[1] = Integer.parseInt(array[1]);
    		return arrayInt;
    	}
    }

     三个实现类:

    public class Plus extends AbstractCalculator implements ICalculator {
     
    	@Override
    	public int calculate(String exp) {
    		int arrayInt[] = split(exp,"\\+");
    		return arrayInt[0]+arrayInt[1];
    	}
    }
    public class Minus extends AbstractCalculator implements ICalculator {
     
    	@Override
    	public int calculate(String exp) {
    		int arrayInt[] = split(exp,"-");
    		return arrayInt[0]-arrayInt[1];
    	}
     
    }
    public class Multiply extends AbstractCalculator implements ICalculator {
     
    	@Override
    	public int calculate(String exp) {
    		int arrayInt[] = split(exp,"\\*");
    		return arrayInt[0]*arrayInt[1];
    	}
    }

    简单的测试类: 

    public class StrategyTest {
     
    	public static void main(String[] args) {
    		String exp = "2+8";
    		ICalculator cal = new Plus();
    		int result = cal.calculate(exp);
    		System.out.println(result);
    	}
    }

    输出:10

    策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。

    N、模板方法模式(Template Method)

    模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。即:一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用,先看个关系图:

     就是在AbstractCalculator类中定义一个主方法calculate,calculate()调用spilt()等,Plus和Minus分别继承AbstractCalculator类,通过对AbstractCalculator的调用实现对子类的调用,看下面的例子:

    public abstract class AbstractCalculator {
    	
    	/*主方法,实现对本类其它方法的调用*/
    	public final int calculate(String exp,String opt){
    		int array[] = split(exp,opt);
    		return calculate(array[0],array[1]);
    	}
    	
    	/*被子类重写的方法*/
    	abstract public int calculate(int num1,int num2);
    	
    	public int[] split(String exp,String opt){
    		String array[] = exp.split(opt);
    		int arrayInt[] = new int[2];
    		arrayInt[0] = Integer.parseInt(array[0]);
    		arrayInt[1] = Integer.parseInt(array[1]);
    		return arrayInt;
    	}
    }
    public class Plus extends AbstractCalculator {
     
    	@Override
    	public int calculate(int num1,int num2) {
    		return num1 + num2;
    	}
    }

     测试类:

    public class StrategyTest {
     
    	public static void main(String[] args) {
    		String exp = "8+8";
    		AbstractCalculator cal = new Plus();
    		int result = cal.calculate(exp, "\\+");
    		System.out.println(result);
    	}
    }

    我跟踪下这个小程序的执行过程:首先将exp和"\\+"做参数,调用AbstractCalculator类里的calculate(String,String)方法,在calculate(String,String)里调用同类的split(),之后再调用calculate(int ,int)方法,从这个方法进入到子类中,执行完return num1 + num2后,将值返回到AbstractCalculator类,赋给result,打印出来。正好验证了我们开头的思路。

    O、观察者模式(Observer)

    观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,会把这种改变通知给其他多个对象,从而影响其他对象的行为。先来看看关系图:

    我解释下这些类的作用:MySubject类就是我们的主对象,Observer1和Observer2是依赖于MySubject的对象,当MySubject变化时,Observer1和Observer2必然变化。AbstractSubject类中定义着需要监控的对象列表,可以对其进行修改:增加或删除被监控对象,且当MySubject变化时,负责通知在列表内存在的对象。我们看实现代码:

    一个Observer接口:

    public interface Observer {
    	public void update();
    }

     两个实现类:

    public class Observer1 implements Observer {
     
    	@Override
    	public void update() {
    		System.out.println("observer1 has received!");
    	}
    }
    public class Observer2 implements Observer {
     
    	@Override
    	public void update() {
    		System.out.println("observer2 has received!");
    	}
    }

     Subject接口及实现类:

    public interface Subject {
    	
    	/*增加观察者*/
    	public void add(Observer observer);
    	
    	/*删除观察者*/
    	public void del(Observer observer);
    	
    	/*通知所有的观察者*/
    	public void notifyObservers();
    	
    	/*自身的操作*/
    	public void operation();
    }
    public abstract class AbstractSubject implements Subject {
     
    	private Vector<Observer> vector = new Vector<Observer>();
    	@Override
    	public void add(Observer observer) {
    		vector.add(observer);
    	}
     
    	@Override
    	public void del(Observer observer) {
    		vector.remove(observer);
    	}
     
    	@Override
    	public void notifyObservers() {
    		Enumeration<Observer> enumo = vector.elements();
    		while(enumo.hasMoreElements()){
    			enumo.nextElement().update();
    		}
    	}
    }
    public class MySubject extends AbstractSubject {
     
    	@Override
    	public void operation() {
    		System.out.println("update self!");
    		notifyObservers();
    	}
     
    }
    public class MySubject extends AbstractSubject {
     
    	@Override
    	public void operation() {
    		System.out.println("update self!");
    		notifyObservers();
    	}
     
    }

     测试类:

    public class ObserverTest {
     
    	public static void main(String[] args) {
    		Subject sub = new MySubject();
    		sub.add(new Observer1());
    		sub.add(new Observer2());
    		
    		sub.operation();
    	}
     
    }

    输出:

    update self!
    observer1 has received!
    observer2 has received!

    P、迭代子模式(Iterator)

    迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。这句话包含两层意思:一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问。我们看下关系图:

    这个思路和我们常用的一模一样,MyCollection中定义了集合的一些操作,MyIterator中定义了一系列迭代操作,且持有Collection实例,我们来看看实现代码:

    两个接口:

    public interface Collection {
    	
    	public Iterator iterator();
    	
    	/*取得集合元素*/
    	public Object get(int i);
    	
    	/*取得集合大小*/
    	public int size();
    }
    public interface Iterator {
    	//前移
    	public Object previous();
    	
    	//后移
    	public Object next();
    	public boolean hasNext();
    	
    	//取得第一个元素
    	public Object first();
    }

     两个实现:

    public class MyIterator implements Iterator {
     
    	private Collection collection;
    	private int pos = -1;
    	
    	public MyIterator(Collection collection){
    		this.collection = collection;
    	}
    	
    	@Override
    	public Object previous() {
    		if(pos > 0){
    			pos--;
    		}
    		return collection.get(pos);
    	}
     
    	@Override
    	public Object next() {
    		if(pos<collection.size()-1){
    			pos++;
    		}
    		return collection.get(pos);
    	}
     
    	@Override
    	public boolean hasNext() {
    		if(pos<collection.size()-1){
    			return true;
    		}else{
    			return false;
    		}
    	}
     
    	@Override
    	public Object first() {
    		pos = 0;
    		return collection.get(pos);
    	}
     
    }
    public class MyCollection implements Collection {
     
    	public String string[] = {"A","B","C","D","E"};
    	@Override
    	public Iterator iterator() {
    		return new MyIterator(this);
    	}
     
    	@Override
    	public Object get(int i) {
    		return string[i];
    	}
     
    	@Override
    	public int size() {
    		return string.length;
    	}
    }

     测试类:

    public class Test {
     
    	public static void main(String[] args) {
    		Collection collection = new MyCollection();
    		Iterator it = collection.iterator();
    		
    		while(it.hasNext()){
    			System.out.println(it.next());
    		}
    	}
    }

    输出:A B C D E

    此处我们貌似模拟了一个集合类的过程,感觉是不是很爽?其实JDK中各个类也都是这些基本的东西,加一些设计模式,再加一些优化放到一起的,只要我们把这些东西学会了,掌握好了,我们也可以写出自己的集合类,甚至框架!

    Q、责任链模式(Chain of Responsibility)

    职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。先看看关系图:

    Abstracthandler类提供了get和set方法,方便MyHandle类设置和修改引用对象,MyHandle类是核心,实例化后生成一系列相互持有的对象,构成一条链。

    public interface Handler {
    	public void operator();
    }
    public abstract class AbstractHandler {
    	
    	private Handler handler;
     
    	public Handler getHandler() {
    		return handler;
    	}
     
    	public void setHandler(Handler handler) {
    		this.handler = handler;
    	}
    	
    }
    public class MyHandler extends AbstractHandler implements Handler {
     
    	private String name;
     
    	public MyHandler(String name) {
    		this.name = name;
    	}
     
    	@Override
    	public void operator() {
    		System.out.println(name+"deal!");
    		if(getHandler()!=null){
    			getHandler().operator();
    		}
    	}
    }
    public class Test {
     
    	public static void main(String[] args) {
    		MyHandler h1 = new MyHandler("h1");
    		MyHandler h2 = new MyHandler("h2");
    		MyHandler h3 = new MyHandler("h3");
     
    		h1.setHandler(h2);
    		h2.setHandler(h3);
     
    		h1.operator();
    	}
    }

    输出:

    h1deal!
    h2deal!
    h3deal!

    此处强调一点就是,链接上的请求可以是一条链,可以是一个树,还可以是一个环,模式本身不约束这个,需要我们自己去实现,同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。

    R、命令模式(Command)

    命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。我们看看关系图:
     

    Invoker是调用者(司令员),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象,看实现代码:

    public interface Command {
    	public void exe();
    }
    public class MyCommand implements Command {
     
    	private Receiver receiver;
    	
    	public MyCommand(Receiver receiver) {
    		this.receiver = receiver;
    	}
     
    	@Override
    	public void exe() {
    		receiver.action();
    	}
    }
    public class Receiver {
    	public void action(){
    		System.out.println("command received!");
    	}
    }
    public class Invoker {
    	
    	private Command command;
    	
    	public Invoker(Command command) {
    		this.command = command;
    	}
     
    	public void action(){
    		command.exe();
    	}
    }
    public class Test {
     
    	public static void main(String[] args) {
    		Receiver receiver = new Receiver();
    		Command cmd = new MyCommand(receiver);
    		Invoker invoker = new Invoker(cmd);
    		invoker.action();
    	}
    }

    输出:command received!

    这个很哈理解,命令模式的目的就是将命令的发出者和执行者之间解耦,实现请求和执行分开,熟悉Struts的同学应该知道,Struts其实就是一种将请求和呈现分离的技术,其中必然涉及命令模式的思想!

    S、备忘录模式(Memento)

    备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。

    Original类是原始类,里面有需要保存的属性value及创建一个备忘录类,用来保存value值。Memento类是备忘录类,Storage类是存储备忘录的类,持有Memento类的实例,该模式很好理解。直接看源码

    public class Original {
    	
    	private String value;
    	
    	public String getValue() {
    		return value;
    	}
     
    	public void setValue(String value) {
    		this.value = value;
    	}
     
    	public Original(String value) {
    		this.value = value;
    	}
     
    	public Memento createMemento(){
    		return new Memento(value);
    	}
    	
    	public void restoreMemento(Memento memento){
    		this.value = memento.getValue();
    	}
    }
    public class Memento {
    	
    	private String value;
     
    	public Memento(String value) {
    		this.value = value;
    	}
     
    	public String getValue() {
    		return value;
    	}
     
    	public void setValue(String value) {
    		this.value = value;
    	}
    }
    public class Storage {
    	
    	private Memento memento;
    	
    	public Storage(Memento memento) {
    		this.memento = memento;
    	}
     
    	public Memento getMemento() {
    		return memento;
    	}
     
    	public void setMemento(Memento memento) {
    		this.memento = memento;
    	}
    }

     测试类:

    public class Test {
     
    	public static void main(String[] args) {
    		
    		// 创建原始类
    		Original origi = new Original("egg");
     
    		// 创建备忘录
    		Storage storage = new Storage(origi.createMemento());
     
    		// 修改原始类的状态
    		System.out.println("初始化状态为:" + origi.getValue());
    		origi.setValue("niu");
    		System.out.println("修改后的状态为:" + origi.getValue());
     
    		// 回复原始类的状态
    		origi.restoreMemento(storage.getMemento());
    		System.out.println("恢复后的状态为:" + origi.getValue());
    	}
    }

    输出:

    初始化状态为:egg
    修改后的状态为:niu
    恢复后的状态为:egg

    简单描述下:新建原始类时,value被初始化为egg,后经过修改,将value的值置为niu,最后倒数第二行进行恢复状态,结果成功恢复了。其实我觉得这个模式叫“备份-恢复”模式最形象。

    T、状态模式(State)

    状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。比如QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态,所以,状态模式就两点:1、可以通过改变状态来获得不同的行为。2、你的好友能同时看到你的变化。看图:

    State类是个状态类,Context类可以实现切换,我们来看看代码:

    /**
     * 状态类的核心类
     */
    public class State {
    	
    	private String value;
    	
    	public String getValue() {
    		return value;
    	}
     
    	public void setValue(String value) {
    		this.value = value;
    	}
     
    	public void method1(){
    		System.out.println("execute the first opt!");
    	}
    	
    	public void method2(){
    		System.out.println("execute the second opt!");
    	}
    }
    /**
     * 状态模式的切换类
     */
    public class Context {
     
    	private State state;
     
    	public Context(State state) {
    		this.state = state;
    	}
     
    	public State getState() {
    		return state;
    	}
     
    	public void setState(State state) {
    		this.state = state;
    	}
     
    	public void method() {
    		if (state.getValue().equals("state1")) {
    			state.method1();
    		} else if (state.getValue().equals("state2")) {
    			state.method2();
    		}
    	}
    }

     测试类:

    public class Test {
     
    	public static void main(String[] args) {
    		
    		State state = new State();
    		Context context = new Context(state);
    		
    		//设置第一种状态
    		state.setValue("state1");
    		context.method();
    		
    		//设置第二种状态
    		state.setValue("state2");
    		context.method();
    	}
    }

    输出:

    execute the first opt!
    execute the second opt!

    根据这个特性,状态模式在日常开发中用的挺多的,尤其是做网站的时候,我们有时希望根据对象的某一属性,区别开他们的一些功能,比如说简单的权限控制等。

    U、访问者模式(Visitor)

    访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。

    访问者模式把数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化。访问者模式适用于数据结构相对稳定算法又易变化的系统。因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。访问者模式将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。其缺点就是增加新的数据结构很困难。—— From 百科

    简单来说,访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。简单关系图:

    来看看原码:一个Visitor类,存放要访问的对象,

    public interface Visitor {
    	public void visit(Subject sub);
    }
    public class MyVisitor implements Visitor {
     
    	@Override
    	public void visit(Subject sub) {
    		System.out.println("visit the subject:"+sub.getSubject());
    	}
    }

    Subject类,accept方法,接受将要访问它的对象,getSubject()获取将要被访问的属性,

    public interface Subject {
    	public void accept(Visitor visitor);
    	public String getSubject();
    }
    public class MySubject implements Subject {
     
    	@Override
    	public void accept(Visitor visitor) {
    		visitor.visit(this);
    	}
     
    	@Override
    	public String getSubject() {
    		return "love";
    	}
    }

    测试:

    public class Test {
     
    	public static void main(String[] args) {
    		
    		Visitor visitor = new MyVisitor();
    		Subject sub = new MySubject();
    		sub.accept(visitor);	
    	}
    }

     输出:visit the subject:love

    该模式适用场景:如果我们想为一个现有的类增加新功能,不得不考虑几个事情:

    1. 新功能会不会与现有功能出现兼容性问题?
    2. 以后会不会再需要添加?
    3. 如果类不允许修改代码怎么办?

    面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦。

    V、中介者模式(Mediator)

    中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和Mediator类的关系,具体类类之间的关系及调度交给Mediator就行,这有点像spring容器的作用。先看看图:

     User类统一接口,User1和User2分别是不同的对象,二者之间有关联,如果不采用中介者模式,则需要二者相互持有引用,这样二者的耦合度很高,为了解耦,引入了Mediator类,提供统一接口,MyMediator为其实现类,里面持有User1和User2的实例,用来实现对User1和User2的控制。这样User1和User2两个对象相互独立,他们只需要保持好和Mediator之间的关系就行,剩下的全由MyMediator类来维护!基本实现:

    public interface Mediator {
    	public void createMediator();
    	public void workAll();
    }
    public class MyMediator implements Mediator {
     
    	private User user1;
    	private User user2;
    	
    	public User getUser1() {
    		return user1;
    	}
     
    	public User getUser2() {
    		return user2;
    	}
     
    	@Override
    	public void createMediator() {
    		user1 = new User1(this);
    		user2 = new User2(this);
    	}
     
    	@Override
    	public void workAll() {
    		user1.work();
    		user2.work();
    	}
    }
    public abstract class User {
    	
    	private Mediator mediator;
    	
    	public Mediator getMediator(){
    		return mediator;
    	}
    	
    	public User(Mediator mediator) {
    		this.mediator = mediator;
    	}
     
    	public abstract void work();
    }
    public class User1 extends User {
     
    	public User1(Mediator mediator){
    		super(mediator);
    	}
    	
    	@Override
    	public void work() {
    		System.out.println("user1 exe!");
    	}
    }
    public class User2 extends User {
     
    	public User2(Mediator mediator){
    		super(mediator);
    	}
    	
    	@Override
    	public void work() {
    		System.out.println("user2 exe!");
    	}
    }

    测试类:

    public class Test {
     
    	public static void main(String[] args) {
    		Mediator mediator = new MyMediator();
    		mediator.createMediator();
    		mediator.workAll();
    	}
    }

    输出:

    user1 exe!
    user2 exe!

    W、解释器模式(Interpreter)

    解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。一般主要应用在OOP开发中的编译器的开发中,所以适用面比较窄。

    Context类是一个上下文环境类,Plus和Minus分别是用来计算的实现,代码如下:

    public interface Expression {
    	public int interpret(Context context);
    }
    public class Plus implements Expression {
     
    	@Override
    	public int interpret(Context context) {
    		return context.getNum1()+context.getNum2();
    	}
    }
    public class Minus implements Expression {
     
    	@Override
    	public int interpret(Context context) {
    		return context.getNum1()-context.getNum2();
    	}
    }
    public class Context {
    	
    	private int num1;
    	private int num2;
    	
    	public Context(int num1, int num2) {
    		this.num1 = num1;
    		this.num2 = num2;
    	}
    	
    	public int getNum1() {
    		return num1;
    	}
    	public void setNum1(int num1) {
    		this.num1 = num1;
    	}
    	public int getNum2() {
    		return num2;
    	}
    	public void setNum2(int num2) {
    		this.num2 = num2;
    	}
    	
    	
    }
    public class Test {
     
    	public static void main(String[] args) {
     
    		// 计算9+2-8的值
    		int result = new Minus().interpret((new Context(new Plus()
    				.interpret(new Context(9, 2)), 8)));
    		System.out.println(result);
    	}
    }

    最后输出正确的结果:3。

    基本就这样,解释器模式用来做各种各样的解释器,如正则表达式等的解释器等等!

    展开全文
  • 常用设计模式

    千次阅读 2020-06-29 21:41:01
    设计模式 从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。 设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联和组合关系的充分理解。 正确使用...

    设计模式

    从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。

    设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联和组合关系的充分理解。

    正确使用设计模式具有一下优点:

    1、可以提高程序员的思维能力、编码能力和设计能力。

    2、使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。

    3、使设计的代码可重用性高、可读性强、可靠行高、灵活性好、可维护性强。

    1、3大类

    创建性模式:

    单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式

    结构型模式:

    适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式,代理模式

    行为型模式:

    模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、接解释器模式、状态模式、策略模式、职责链模式、访问者模式。

    2、OOP七大法则

    • 开闭原则:对扩展开放,对修改关闭。
    • 里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立。
    • 依赖倒置原则:要面向接口编程,不要面向实现编程。
    • 单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性。
    • 接口隔离原则:要为各个类建立他们需要专用接口。
    • 迪米特法则:只与你的朋友直接交谈,不跟陌生人说话。
    • 合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

    3、单例模式

    按照惯有的分类方式,设计模式总共分为3大类:1、创建型 2、结构型 3、行为型。

    单例模式便是创建型设计模式的一种,它确保某一个类在系统中只有一个实例,并自行实例化,同时向外部提供获取这个唯一实例的接口。从这段描述中,我们不难可以得到单例模式的三大特性:

    单例类只有一个实例。
    单例类必须自己实例化自己。
    单例类需要向外提供实例。

    3.1、饿汉式

    /**
     * 上面是饿汉式单例模式的标准代码,
     * EagerSingleton类的实例因为变量instance申明为static的关系,
     * 在类加载过程中便会执行。由此带来的好处是Java的类加载机制本身为我们保证了实例化过程的线程安全性,
     * 缺点是这种空间换时间的方式,即使类实例本身还未用到,实例也会被创建。
     * */
    class EagerSingleton {
        // 静态变量,类在创建之初就会执行实例化动作。
        private static EagerSingleton instance = new EagerSingleton();
        // 私有化构造函数,使外界无法创建实例
        private EagerSingleton(){}
        // 为外界提供获取实例接口
        public static EagerSingleton getInstance(){
            return instance;
        }
    }
    

    3.2、懒汉式

    /**
     * 懒汉式
     * 这个缺点的原因,涉及到并发编程的原子性。
     * 实例中,创建实例的代码逻辑失去了原子性从而导致可能存在多个实例创建的情况。
     * */
    public class LazySingleton {
        private static LazySingleton instance = null;
        private LazySingleton(){
            System.out.println(Thread.currentThread().getName()+"ok");
        }
        // 为外界提供获取实例接口
        public static LazySingleton getInstance(){
            if(instance == null){
                instance = new LazySingleton(); // 懒加载
            }
            return instance;
        }
    
        /**
         * 多线程并发
         * 发生了多次实例化对象
         * */
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(()->{
                    LazySingleton.getInstance();
                }).start();
            }
        }
    
    }
    

    3.3、懒汉式加锁

    /**
     * 双重检测锁
     * */
    class Singleton2 {
    
        //volatile 原子性操作
        private volatile static Singleton2 instance = null;
    
        /**
         * 设置标志位
         * */
        private static boolean youyuan = false;
    
        private Singleton2(){
            synchronized (Singleton2.class){
                if (youyuan == false){
                    youyuan = true;
                }else {
                    throw new RuntimeException("不要试图使用反射破坏异常");
                }
            }
            System.out.println(Thread.currentThread().getName()+"ok");
        }
    
        public static Singleton2 getInstance(){
            //先检查实例是否存在,如果不存在才进入下面的同步块
            if(instance == null){
                //同步块,线程安全的创建实例
                synchronized (Singleton2.class) {
                    //再次检查实例是否存在,如果不存在才真正的创建实例
                    if(instance == null){
                        instance = new Singleton2();//不是一个原子性操作
                    }
                }
            }
            return instance;
        }
    
        /**
         * 线程安全
         * */
    //    public static void main(String[] args) {
    //        for (int i = 0; i < 10; i++) {
    //            new Thread(()->{
    //                Singleton2.getInstance();
    //            }).start();
    //        }
    //    }
    
    	//可以通过反射破坏
        public static void main(String[] args) throws Exception{
            //Singleton2 instance = Singleton2.getInstance();
            Constructor<Singleton2> declaredConstructor = Singleton2.class.getDeclaredConstructor();
            Field youyuan = Singleton2.class.getDeclaredField("youyuan");
            youyuan.setAccessible(true);
            declaredConstructor.setAccessible(true);
    
            Singleton2 singleton2 = declaredConstructor.newInstance();
            //修改字段
            youyuan.set(singleton2,false);
            Singleton2 singleton21 = declaredConstructor.newInstance();
            System.out.println(singleton21);
            System.out.println(singleton2);
        }
    
    }
    
    

    3.4、静态内部类

    /**
     * 静态内部类
     * */
    class Singleton3 {
        private Singleton3(){
            System.out.println(Thread.currentThread().getName()+"ok");
        }
        // 只有当类被调用时,才会加载
        private static class SingletonHolder{
            // 静态初始化器,由JVM来保证线程安全
            private static Singleton3 instance = new Singleton3();
        }
    
        public static Singleton3 getInstance(){
            return SingletonHolder.instance;
        }
    }
    

    3.5、枚举类

    /**
     * 枚举类,本身也是一个类,不能通过反射获取字段
     * */
    enum Singleton {
        uniqueInstance;
        public Singleton singletonOperation(){
            // 单例类的其它操作
            return uniqueInstance;
        }
    
        public static void main(String[] args) throws Exception{
            Constructor<Singleton> declaredConstructor = 			Singleton.class.getDeclaredConstructor(String.class,int.class);
            declaredConstructor.setAccessible(true);
            Singleton singleton = declaredConstructor.newInstance();
            System.out.println(singleton);
        }
    }
    

    4、工厂模式

    实现了创建者和调用者的分离。

    4.1、简单工厂模式

    用来生产同一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PAANcWdN-1593437594857)(C:\Users\游建成\AppData\Roaming\Typora\typora-user-images\image-20200626104132899.png)]

    Car接口

    public interface Car {
        public void carName();
    }
    

    Car实现类

    public class Wuling implements Car {
        @Override
        public void carName() {
            System.out.println("Wuling");
        }
    }
    

    静态工厂

    public class CarFactory {
    
         public static Car getCar(String name){
            if (name.equals("DaZhong")){
                return new DaZhong();
            }else if (name.equals("Wuling")){
                return new Wuling();
            }else{
                return null;
            }
        }
    
    }
    

    测试

    public class Main {
        public static void main(String[] args) {
    //        Wuling wuling = new Wuling();
    //        wuling.carName();
            Car wuling = CarFactory.getCar("Wuling");
            wuling.carName();
        }
    }
    

    4.2、工厂方法模式

    用来生产同一等级结构中的固定产品(支持增加任意产品)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YCyY3Mxk-1593437594872)(C:\Users\游建成\AppData\Roaming\Typora\typora-user-images\image-20200626104417199.png)]

    4.3、抽象工厂模式

    围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。

    抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定他们具体的类。

    适用场景:

    • 客户端不依赖于产品类实例如何被创建、实现等细节。

    • 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码。

    • 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现。

    优点

    • 具体产品在应用层的代码隔离,无需关心创建的细节。

    • 将一个系列的产品统一到一起创建。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ld3Fx54s-1593437594877)(C:\Users\游建成\AppData\Roaming\Typora\typora-user-images\image-20200626112717211.png)]

    4.4、小结

    小结

    1、简单工厂模式(静态工厂模式):虽然某种程度上不符合设计原则,但实际使用最多。

    2、工厂方法模式:不修改已有类的前提上,通过增加新的工厂类实现扩展。

    3、抽象工厂模式:不可以增加产品,可以增加产品族。

    应用场景

    1、JDk中Calendar的getInstance方法

    2、JDBC中的Connection对象的获取

    3、Spring中IOC容器创建管理bean对象

    4、反射中Class对象的newInstance方法

    核心本质

    • 实例化对象不使用new,用工厂方法替代。

    • 将选择实现类,创建对象统一管理和控制,从而将调用者跟我们的实现类解耦。

    5、建造者模式

    建造者模式也属于创建型模式,他提供了一种创建对象的最佳方式。

    定义

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

    主要作用

    在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的创建过程和细节隐藏起来)。

    例子

    工厂(建造者模式):负责制造汽车。

    汽车购买者(用户):只需要说出你需要的型号,然后直接购买就可以使用了

    5.1、监视者模式

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jM27fzwb-1593437594887)(C:\Users\游建成\AppData\Roaming\Typora\typora-user-images\image-20200627113154292.png)]

    指挥

    //指挥:核心。负责指挥构建一个工程,工程如何构建,由它决定
    public class Director {
        //指挥工人按照顺序建房子
        public Product build(Builder builder){
            builder.buildA();
            builder.buildB();
            builder.buildC();
            builder.buildD();
            return builder.getProduct();
        }
    }
    

    具体的建造者

    //抽象的建造者:方法
    public abstract class Builder {
        abstract void buildA();  //地基
        abstract void buildB();  //钢筋水泥
        abstract void buildC();  //铺电线
        abstract void buildD();  //粉刷
    
        //完工:得到产品
        abstract Product getProduct();
    }
    
    //具体的建造者:工人
    public class Worker extends Builder {
    
        private Product product;
    
        public Worker() {
            this.product = new Product();
        }
    
        @Override
        void buildA() {
            product.setBuildA("地基");
            System.out.println("地基");
        }
    
        @Override
        void buildB() {
            product.setBuildA("钢筋水泥");
            System.out.println("钢筋水泥");
        }
    
        @Override
        void buildC() {
            product.setBuildA("铺电线");
            System.out.println("铺电线");
        }
    
        @Override
        void buildD() {
            product.setBuildA("粉刷");
            System.out.println("粉刷");
        }
    
        @Override
        Product getProduct() {
            return product;
        }
    }
    

    产品

    package org.javaboy.builder;
    
    /**
     * @Author you猿
     * @Date 2020/6/27 12:09
     */
    
    //产品:房子
    public class Product {
        private String buildA;  //地基
        private String buildB;  //钢筋水泥
        private String buildC;  //铺电线
        private String buildD;  //粉刷
    
        public String getBuildA() {
            return buildA;
        }
    
        public void setBuildA(String buildA) {
            this.buildA = buildA;
        }
    
        public String getBuildB() {
            return buildB;
        }
    
        public void setBuildB(String buildB) {
            this.buildB = buildB;
        }
    
        public String getBuildC() {
            return buildC;
        }
    
        public void setBuildC(String buildC) {
            this.buildC = buildC;
        }
    
        public String getBuildD() {
            return buildD;
        }
    
        public void setBuildD(String buildD) {
            this.buildD = buildD;
        }
    
        @Override
        public String toString() {
            return "Product{" +
                    "buildA='" + buildA + '\'' +
                    ", buildB='" + buildB + '\'' +
                    ", buildC='" + buildC + '\'' +
                    ", buildD='" + buildD + '\'' +
                    '}';
        }
    }
    
    

    测试

    public class Test {
        public static void main(String[] args) {
            //指挥
            Director director = new Director();
            //指挥 具体的工人完成 产品
            Product build = director.build(new Worker());
            System.out.println(build.toString());
        }
    }
    

    5.2、匿名模式

    建造者

    //建造者
    public abstract class Builder {
        abstract Builder builderA(String msg);  //汉堡
        abstract Builder builderB(String msg);  //可乐
        abstract Builder builderC(String msg);  //薯条
        abstract Builder builderD(String msg);  //甜点
    
        abstract Product getProduct();
    
    }
    
    
    public class Worker extends Builder {
    
        private Product product;
    
        public Worker( ) {
            this.product = new Product();
        }
    
    
        @Override
        Builder builderA(String msg) {
            product.setBuilderA(msg);
            return this;
        }
    
        @Override
        Builder builderB(String msg) {
            product.setBuilderB(msg);
            return this;
        }
    
        @Override
        Builder builderC(String msg) {
            product.setBuilderC(msg);
            return this;
        }
    
        @Override
        Builder builderD(String msg) {
            product.setBuilderD(msg);
            return this;
        }
    
        @Override
        Product getProduct() {
            return product;
        }
    }
    
    

    产品

    package org.javaboy.builder.demo2;
    
    /**
     * @Author you猿
     * @Date 2020/6/27 20:13
     */
    public class Product {
    
        private String builderA = "汉堡";
        private String builderB = "可乐";
        private String builderC = "薯条";
        private String builderD = "甜点";
    
        public String getBuilderA() {
            return builderA;
        }
    
        public void setBuilderA(String builderA) {
            this.builderA = builderA;
        }
    
        public String getBuilderB() {
            return builderB;
        }
    
        public void setBuilderB(String builderB) {
            this.builderB = builderB;
        }
    
        public String getBuilderC() {
            return builderC;
        }
    
        public void setBuilderC(String builderC) {
            this.builderC = builderC;
        }
    
        public String getBuilderD() {
            return builderD;
        }
    
        public void setBuilderD(String builderD) {
            this.builderD = builderD;
        }
    
        @Override
        public String toString() {
            return "Product{" +
                    "builderA='" + builderA + '\'' +
                    ", builderB='" + builderB + '\'' +
                    ", builderC='" + builderC + '\'' +
                    ", builderD='" + builderD + '\'' +
                    '}';
        }
    }
    

    测试

     public class Test {
        public static void main(String[] args) {
            //服务员
            Worker worker = new Worker();
            //链式编程
            Product product = worker.builderA("冰淇淋").builderB("烤鸡腿")
                    .getProduct();
            System.out.println(product.toString());
        }
    }
    
    

    5.3、优缺点

    优点

    • 产品的建造和表示分离,实现了解耦。
    • 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰。
    • 具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合开闭原则。

    缺点

    • 创建的产品一般具有较多的共同点,其组成部分相似:如果产品之间差异性很大,不适用于建造者模式。

    5.4、应用场景

    应用场景:

    • 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;

    • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

    • 适合于一个具有较多的零件(属性)的产品(对象)的创建过程。

    建造者与抽象工厂模式的比较:

    • 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。

    • 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于-步步构造一 个复杂对象,返回-一个完整的对象。

    • 如果将抽象工厂模式看成汽车配件生产工厂,生产-一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回-辆完整的汽车!

    6、原型模式

    Prototype、Cloneable接口、clone()方法、 克隆

    6.1、浅克隆

    1、实体类

    /**
     * 1、实现一个接口 Cloneable
     * 2、重写一个方法
     * */
    //video
    public class Video implements Cloneable {
        private String name;
        private Date createTime;
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        public Video(String name, Date createTime) {
            this.name = name;
            this.createTime = createTime;
        }
    
        public Video() {
    
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Date getCreateTime() {
            return createTime;
        }
    
        public void setCreateTime(Date createTime) {
            this.createTime = createTime;
        }
    
        @Override
        public String toString() {
            return "Video{" +
                    "name='" + name + '\'' +
                    ", createTime=" + createTime +
                    '}';
        }
    }
    
    

    2、克隆

    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象 v1
        Date date = new Date();
        Video v1 = new Video("youyuan", date);
        //v1 克隆 v2
        Video v2 = (Video) v1.clone();//克隆出来的对象和原来是一模一样的
        System.out.println("v1="+v1);
        System.out.println("v2="+v2);
        System.out.println("=========================");
        date.setTime(12435);
        System.out.println("v1="+v1);
        System.out.println("v2="+v2);
    }
    

    6.2、深克隆

    • 序列化与反序列化
    • 改造clone()方法

    1、实体类

     /**
     * 1、实现一个接口 Cloneable
     * 2、重写一个方法
     * */
    //video
    public class Video implements Cloneable {
        private String name;
        private Date createTime;
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Object obj = super.clone();
            //实现深克隆~
            Video v = (Video) obj;
            v.createTime = ((Date) this.createTime.clone());
            return obj;
        }
    
        public Video(String name, Date createTime) {
            this.name = name;
            this.createTime = createTime;
        }
    
        public Video() {
    
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Date getCreateTime() {
            return createTime;
        }
    
        public void setCreateTime(Date createTime) {
            this.createTime = createTime;
        }
    
        @Override
        public String toString() {
            return "Video{" +
                    "name='" + name + '\'' +
                    ", createTime=" + createTime +
                    '}';
        }
    }
    
    

    2、克隆

    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象 v1
        Date date = new Date();
        Video v1 = new Video("youyuan", date);
        //v1 克隆 v2
        Video v2 = (Video) v1.clone();//克隆出来的对象和原来是一模一样的
        System.out.println("v1="+v1);
        System.out.println("v2="+v2);
        System.out.println("=========================");
        date.setTime(12435);
        System.out.println("v1="+v1);
        System.out.println("v2="+v2);
    }
    

    7、配器模式

    将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作!

    7.1、角色分析

    • 目标接口:客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口。(USB接口)
    • 需要适配的类:需要适配的类或者适配者类。(网线)
    • 适配器:通过包装一个需要适配的对象,把原接口转换成目标对象。

    7.2、对象适配器的优点

    • 一个对象适配器可以把多个不同的适配者适配到同一目标
    • 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,适配者的子类也可通过该适配器进行适配。

    7.3、类适配器缺点

    • 一次最多只能适配一个适配者类,不能同时适配多个适配者。
    • 类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。

    7.4、适用场景

    • 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
    • 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可以在将来引进的类一起工作。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uk8XE2OP-1593437594893)(C:\Users\游建成\AppData\Roaming\Typora\typora-user-images\image-20200521193240903.png)]

    package org.javaboy.adapter;
    
    /**
     * @Author you猿
     * @Date 2020/5/21 18:43
     */
    //1. 继承(类适配器,单继承)
    //2. 组合(对象适配器:常用)
    //真正的适配者,需要连接USB,连接网线
    public class Adapter implements NetToUsb {
        //extend Adaptee  类适配器
        //网线
        private Adaptee adaptee;
    
        public Adapter(Adaptee adaptee) {
            this.adaptee = adaptee;
        }
    
        @Override
        public void handleRequest() {
            //super.connectInternet();
            this.adaptee.connectInternet();
        }
    
        public static void main(String[] args) {
    //        Computer computer = new Computer();   //电脑
    //        Adaptee adaptee = new Adaptee();      //网线
    //        Adapter adapter = new Adapter();      //转换接口
    //        computer.net(adapter);
            Computer computer = new Computer();   //电脑
            Adaptee adaptee = new Adaptee();      //网线
            Adapter adapter = new Adapter(adaptee);
            computer.net(adapter);
        }
    }
    
    //网线,要被适配的类。
    class Adaptee {
        public void connectInternet(){
            System.out.println("连接到网络");
        }
    }
    
    //客户端类,想上网,插不上网线
    class Computer {
        //电脑需要连接上转接器才可以上网
        public void net(NetToUsb netToUsb){
            //上网的实现,找个转接头
             netToUsb.handleRequest();
        }
    }
    
    //接口转换器的抽象实现
    interface NetToUsb{
        //作用:处理请求,将网线插入usb
        public void handleRequest();
    }
    
    

    8、桥接模式

    桥接模式是将抽象部分与它的实现分离,使得他们都可以独立地变化。它是一种对象结构型模式,又称为柄提模式或接口模式。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pQ81NsBR-1593437594899)(C:\Users\游建成\AppData\Roaming\Typora\typora-user-images\image-20200629100354115.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jGay3INj-1593437594903)(C:\Users\游建成\AppData\Roaming\Typora\typora-user-images\image-20200629105026475.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7jhe4hA6-1593437594909)(C:\Users\游建成\AppData\Roaming\Typora\typora-user-images\image-20200629103202890.png)]

    品牌

    //品牌
    public interface Brand {
        void info();
    }
    
    //苹果品牌
    public class Apple implements Brand {
        @Override
        public void info() {
            System.out.print("苹果");
        }
    }
    
    //联想品牌
    public class Lenovo implements Brand {
        @Override
        public void info() {
            System.out.print("联想");
        }
    }
    
    

    电脑类型

    //抽象的电脑类型类
    public abstract class Computer {
        //组合 品牌
        protected Brand brand;
    
        public Computer(Brand brand) {
            this.brand = brand;
        }
    
        public void info(){
            brand.info();//自带品牌
        }
    }
    
    //台式
    class Desktop extends Computer{
    
        public Desktop(Brand brand) {
            super(brand);
        }
    
        @Override
        public void info() {
            super.info();
            System.out.println("台式机");
        }
    }
    
    //苹果
    class Laptop extends Computer{
    
        public Laptop(Brand brand) {
            super(brand);
        }
    
        @Override
        public void info() {
            super.info();
            System.out.println("笔记本");
        }
    }
    

    组合

    public class Test {
        public static void main(String[] args) {
            //苹果 笔记本
            Computer computer1 = new Laptop(new Apple());
            computer1.info();
            //联想台式机
            Computer computer2 = new Desktop(new Lenovo());
            computer2.info();
        }
    }
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZkFEEqMJ-1593437594917)(C:\Users\游建成\AppData\Roaming\Typora\typora-user-images\image-20200629103137563.png)]

    8.1、最佳实践

    • 如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使必们在抽象层建立-个关联关系。抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一 个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。

    • 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。

    • 虽然在系统中使用继承是没有问题的, 但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。

    场景:

    1、Java语言通过Java虚拟机实现了平台的无关性。

    2、AWT中的Peer架构

    3、JDBC驱动程序也是桥接模式的应用之一。

    8.2、优缺点

    好处分析:

    • 桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本。

    • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像- -座桥,可以把两个变化的维度连接起来!

    劣势分析:

    • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。

    • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

    9、静态代理

    角色分析:

    • 抽象角色:一般使用接口或者抽象类来解决

    • 真实角色:被代理的角色

    • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作。

    • 客户:访问代理对象的人。

    代码步骤

    1、接口

    //租房
    public interface Rent {
        public void rent();
    }
    

    2、真实角色

    public class Host implements Rent {
        @Override
        public void rent() {
            System.out.println("房东要出租房子");
        }
    }
    

    3、中介

    public class Proxy implements Rent{
    
        private Host host;
    
        public Proxy(){
    
        }
    
        public Proxy(Host host) {
            this.host = host;
        }
    
        @Override
        public void rent() {
            host.rent();
        }
    
        //看房
        public void seeHouse(){
            System.out.println("中介带你看房");
        }
    
        //签合同
        public void heTong(){
            System.out.println("签合同");
        }
    
        //收中介费
        public void fare(){
            System.out.println("收中介费");
        }
    }
    
    

    4、客户端访问代理角色

    public class Client {
        public static void main(String[] args) {
            //房东要租房子
            Host host = new Host();
            //代理,中介帮房东租房子
            Proxy proxy = new Proxy(host);
            //租房直接找中介
            proxy.rent();
        }
    }
    
    

    代理模式的好处:

    • 可以使真实的操作更加纯粹,不用去关注一些公共的业务。
    • 公共也就交给代理角色,实现了业务的分工。
    • 公共业务发生扩展的时候,方便集中管理。

    缺点:

    • 一个真实的角色就会产生一个代理角色。代码量会翻倍,开发效率会变低

    9.1、AOP实例

    AOP的实现

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mBgwmIf3-1593437594923)(C:\Users\游建成\AppData\Roaming\Typora\typora-user-images\image-20200629164606979.png)]

    代码:

    public interface UserService {
        public void add();
        public void update();
        public void delete();
        public void query();
    }
    
    package org.javaboy.staticproxy.demo2;
     
    //真实对象
    public class UserServiceImpl implements UserService {
        @Override
        public void add() {
            System.out.println("add");
        }
    
        @Override
        public void update() {
            System.out.println("update");
        }
    
        @Override
        public void delete() {
            System.out.println("delete");
        }
    
        @Override
        public void query() {
            System.out.println("query");
        }
    }
    
    //代理对象
    public class UserServiceProxy implements UserService {
    
        private UserServiceImpl userService;
    
        public UserServiceProxy(UserServiceImpl userService) {
            this.userService = userService;
        }
    
        @Override
        public void add() {
            log("日志");
            System.out.println("add");
        }
    
        @Override
        public void update() {
            System.out.println("update");
        }
    
        @Override
        public void delete() {
            System.out.println("delete");
        }
    
        @Override
        public void query() {
            System.out.println("query");
        }
    
        //日志方法
        public void log(String msg){
            System.out.println("使用了"+msg+"方法");
        }
    
        public void setUserService(UserServiceImpl userService) {
            this.userService = userService;
        }
    }
    
    

    测试:

    public class Client {
        public static void main(String[] args) {
            UserServiceProxy userServiceProxy = new UserServiceProxy(new UserServiceImpl());
            userServiceProxy.add();
    
        }
    }
    

    10、动态代理

    • 动态代理和静态代理角色一样

    • 动态代理的代理类是动态生成的,不是我们直接写好的。

    • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理。

      1、基于接口 =》JDK动态代理【使用】

      2、基于类:cglib

      3、java字节码实现:javasist

    需要了解两个类:

    1. Proxy:代理
    2. InvocationHandler:调用处理程序。

    10.1、租房实例

    代码

    //房东
    public class Host implements Rent {
        @Override
        public void rent() {
            System.out.println("房东要出租房子");
        }
    }
    
    //租房
    public interface Rent {
        public void rent();
    }
    
    
    //用这个类,自动生成代理类!
    public class ProxyInvocationHandler implements InvocationHandler {
    
        //被代理的接口
        private Object target;
    
        public void setTarget(Object target) {
            this.target = target;
        }
    
        //生成得到代理类
        public Object getProxy(){
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),this);
        }
    
        //处理代理实例,并返回结果
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //动态代理的本质,就是使用反射机制实现
            Object res = method.invoke(target, args);
            return res;
        }
    }
    
    //测试
    public class Client {
        public static void main(String[] args) {
            //真实角色
            Host host = new Host();
            //代理角色:未创建
            ProxyInvocationHandler handler = new ProxyInvocationHandler();
            //通过调用程序处理角色来处理我们要调用的接口对象
            handler.setTarget(host);
            //这里的proxy就是动态生成的,并没有写(面向接口编程)
            Rent proxy = (Rent) handler.getProxy();
            proxy.rent();
        }
    }
    
    

    封装接口

    //用这个类,自动生成代理类!
    public class ProxyInvocationHandler implements InvocationHandler {
    
        //被代理的接口
        private Object target;
    
        public void setTarget(Object target) {
            this.target = target;
        }
    
        //生成得到代理类
        public Object getProxy(){
            return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),this);
        }
    
        //处理代理实例,并返回结果
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(method.getName());
            //动态代理的本质,就是使用反射机制实现
            Object res = method.invoke(target, args);
            return res;
        }
    
    }
    
    //测试
    public class Client {
        public static void main(String[] args) {
            //真实角色
            UserServiceImpl userService = new UserServiceImpl();
            //代理角色,不存在
            ProxyInvocationHandler handler = new ProxyInvocationHandler();
            //设置代理对象
            handler.setTarget(userService);
            //动态生成代理类
            UserService proxy = (UserService) handler.getProxy();
            proxy.query();
        }
    }
    
    展开全文
  • 软件设计模式详解

    万次阅读 多人点赞 2018-11-07 21:40:21
    软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 ...

    软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性

    简介

    设计模式(英语 design pattern)是对面向对象设计中反复出现的问题的解决方案。这个术语是在1990年代由Erich Gamma等人从建筑设计领域引入到计算机科学中来的。这个术语的含义还存有争议。算法不是设计模式,因为算法致力于解决问题而非设计问题。设计模式通常描述了一组相互紧密作用的类与对象。设计模式提供一种讨论软件设计的公共语言,使得熟练设计者的设计经验可以被初学者和其他设计者掌握。设计模式还为软件重构提供了目标。

    随着软件开发社群对设计模式的兴趣日益增长,已经出版了一些相关的专著,定期召开相应的研讨会,而且Ward Cunningham为此发明了WikiWiki用来交流设计模式的经验。

    历史

    肯特·贝克和沃德·坎宁安在1987年利用克里斯托佛·亚历山大在建筑设计领域里的思想开发了设计模式并把此思想应用在Smalltalk中的图形用户接口的生成中。一年后Erich Gamma在他的苏黎世大学博士毕业论文中开始尝试把这种思想改写为适用于软件开发。于此同时James Coplien 在1989年至1991 年也在利用相同的思想致力于C++的开发,而后于1991年发表了他的著作Advanced C++ Idioms。就在这一年Erich Gamma 得到了博士学位,然后去了美国,在那与Richard Helm, Ralph Johnson ,John Vlissides合作出版了Design Patterns - Elements of Reusable Object-Oriented Software 一书,在此书中共收录了23个设计模式。这四位作者在软件开发领域里也以他们的匿名著称Gang of Four(四人帮,简称GoF),并且是他们在此书中的协作导致了软件设计模式的突破。有时这个匿名GoF也会用于指代前面提到的那本书。

    模式格式

    尽管名称和顺序在不同的资料中各有不同,描述模式的格式大致分为以下四个主要部分:

    模式名称(Pattern Name):每一个模式都有自己的名字,模式的名字使得我们可以讨论我们的设计。

    问题(Problem):在面向对象的系统设计过程中反复出现的特定场合,它导致我们采用某个模式。

    解决方案(Solution):上述问题的解决方案,其内容给出了设计的各个组成部分,它们之间的关系、职责划分和协作方式

    效果(Consequence):采用该模式对软件系统其他部分的影响,比如对系统的扩充性、可移植性的影响。影响也包括负面的影响。

    别名(Also Known As):一个模式可以有超过一个以上的名称。这些名称应该要在这一节注明。

    动机(Motivation):该模式应该利用在哪种情况下是本节提供的方案(包括问题与来龙去脉)的责任。

    应用(Applicability)

    结构(Structure):这部分常用类图与互动图阐述此模式。

    参与者(Participants):这部分提供一份本模式用到的类与物件清单,与它们在设计下扮演的角色。

    合作(Collaboration):描述在此模式下,类与物件间的互动。

    结果(Consequences):这部分应描述使用本模式後的结果、副作用、与交换(trade-off)

    实现(Implementaion):这部分应描述实现该模式、该模式的部分方案、实现该模式的可能技术、或者建议实现模式的方法。

    例程(Sample Code):示范程式。

    已知应用(Known Uses):业界已知的实做范例。

    相关模式(Related Patterns):这部分包括其他相关模式,以及与其他类似模式的不同。

    模式列表

           设计模式分为三大类:创建型、结构型、行为型。创建型模式处理的是对象的创建过程(通过各种方式创建对象,使对象创建和管理变得简单),结构型模式处理的是对象/类的组合,行为型模式处理类和对象间的交互方式和任务分布(只有类可以创建对象,接口只能被实现)

    创建型

    抽象工厂模式(Abstract Factory)

           抽象工厂模式是提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。区别于工厂方法模式的地方,工厂方法模式是创建一个工厂,可以实现多种对象;而抽象工厂模式是提供一个抽象工厂接口,里面定义多种工厂,每个工厂可以生产多种对象前者的重点在于"怎么生产",后者的重点在于"生产哪些";前者是一个抽象产品类,可以派生出多个具体产品类,后者是多个抽象产品类,每个抽象产品类可以派生出多个具体产品类;前者的每个具体工厂类只能创建一个具体产品类的实例,后者的每个具体工厂类可以创建多个具体产品类的实例。

    工厂方法模式(Factory Method)

           工厂方法模式的创建是因为简单工厂模式有一个问题,在简单工厂模式中类的创建依赖工厂类,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则,所以就出现了工厂方法模式,只需要创建一个工厂接口和多个工厂实现类,子类可以自己决定实例化哪一个工厂类,client类针对抽象接口进行编程,如果需要增加新的功能,继承工厂接口,直接增加新的工厂类就可以了,创建过程延迟到子类中进行,不需要修改之前的代码,满足了开闭原则,达到灵活地生产多种对象。。

    原型模式 (Prototype)

           原型模式是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。其实就是将对象复制了一份并返还给调用者,对象需继承Cloneable并重写clone()方法。原型模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。分为浅复制和深复制,前者是将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的;后者是将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。

    单例模式(Singleton)

           单例模式能保证一个类仅有一个实例,并提供一个访问它的全局访问点,同时在类内部创造单一对象,通过设置权限,使类外部无法再创造对象。单例对象能保证在一个JVM中,该对象只有一个实例存在,这样做的好处就在于如果某些类创建比较频繁,特别是对于一些大型的对象,这是一笔很大的系统开销。在创建的时候,省去了new操作符,降低了系统内存的使用频率,减轻了系统的压力。同时单例模式保证在一个jvm中仅存在一个实例的好处就在于好比一个军队当中只会存在一个最高级别的军官来指挥整个军队,这样才能保证独立控制整个过程,否则如果出现多个,肯定会杂乱无序。

    建造者模式(Builder)

           建造者模式是将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示,就好比是每个饭店或者每家都会做西红柿炒鸡蛋,最后虽然都是西红柿炒鸡蛋的外观,但是由于当中的做饭过程存在差别,所以味道会不同。在程序当中就是将一些不会变的基本组件,通过builder来进行组合,构建复杂对象,实现分离。这样做的好处就在于客户端不必知道产品内部组成的细节;同时具体的建造者类之间是相互独立的,对系统的扩展非常有利,满足开闭原则;由于具体的建造者类是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。

    结构型

    适配器模式 (Adapter)

           将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

    桥接模式(Bridge)

           桥接模式是将抽象部分与实现部分分离,使它们都可以独立的变化。桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化(突然联想到了mvc模式)。将抽象化与实现化解耦,使得二者可以独立变化,就好比现在常说的mvc模式,view和model之间通过control来控制,达到高内聚低耦合来解耦的目的。

    组合模式(Composite)

           组合模式是将对象组合成树形结构以表示"部分-整体"的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。创建了一个包含自己对象组的类,并提供修改对象组的方法。在系统的文件和文件夹的问题上就使用了组合模式,文件下不可以有对象,而文件夹下可以有文件对象或者文件夹对象。

    修饰模式 (Decorator)

           装饰器模式是动态地给一个对象添加一些额外的职责,给一个对象增加一些新的功能,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。除了动态的增加,也可以动态的撤销,要做到动态的形式,不可以用继承实现,因为继承是静态的。

    外观模式

           外观模式是为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。在客户端和复杂系统之间再加一层,提供一个容易使用的外观层。外观模式是为了解决类与类之间的依赖关系的,外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,比如搜狐门户网站,就利用了外观模式。

    享元模式

           享元模式是运用共享技术有效地支持大量细粒度的对象。享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,重用现有的同类对象,若未找到匹配的对象,则创建新对象,这样可以减少对象的创建,降低系统内存,提高效率。

    代理模式(Proxy)

            代理模式是为其他对象提供一种代理以控制对这个对象的访问,也就是创建类的代理类,间接访问被代理类的过程中,对其功能加以控制。它和装饰器模式的区别在于,装饰器模式为了增强功能,而代理模式是为了加以控制。代理模式就是多一个代理类出来,替原对象进行一些操作,例如买火车票不一定在火车站买,也可以去代售点。再比如打官司需要请律师,因为律师在法律方面有专长,可以替我们进行操作。

    行为模式

    责任链模式 (Chain of Responsibility)

           责任链模式是避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求。在生活中学生进行请假的过程中,会涉及到,学生请假会一级一级往上批,最终处理,具体由谁批准可能不清楚。在程序当中,现在使用的struts拦截器即用到了责任链模式

    命令模式 (Command)

           命令模式是将一个请求封装成一个对象,从而使发出者可以用不同的请求对客户进行参数化。模式当中存在调用者、接收者、命令三个对象,实现请求和执行分开;调用者选择命令发布,命令指定接收者。举个例子,司令员下令让士兵去干件事情,司令员的作用是发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。司令士兵命令三者相互解耦,任何一方都不用去依赖其他人。其实struts框架也涉及到命令模式的思想。

    解释器模式(Interpreter)

           解释器模式是给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子,基本也就用在这个范围内,适用面较窄,例如:正则表达式的解释等。

    迭代器模式(Iterator)

           迭代器模式是提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。在Java当中,将聚合类中遍历各个元素的行为分离出来,封装成迭代器,让迭代器来处理遍历的任务;使简化聚合类,同时又不暴露聚合类的内部,在我们经常使用的JDK中各个类也都是这些基本的东西。

    中介者模式(Mediator)

           中介者模式是用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。例如,MVC模式中control就是model和view的中介者。与适配器区别在于,适配器是为了兼容不同的接口,而中介者是为了将显示和操作分离

    备忘录模式 (Memento)

           备忘录模式是在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。创建一个备忘录类,用来存储原始类的信息;同时创建备忘录仓库类,用来存储备忘录类,主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象,也就是做个备份。在系统当中使用的撤销操作,即是使用了备忘录模式,系统可以保存有限次数的文件状态,用户可以进行上几个状态的恢复,也就是用到了备忘录模式。

    观察者模式(Observer)

           观察者模式是定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。也就是当被观察者状态变化时,通知所有观察者,这种依赖方式具有双向性,在QQ邮箱中的邮件订阅和RSS订阅,当我们浏览一些博客时,经常会看到RSS图标,意思就是,当你订阅了该文章,如果后续有更新,会及时通知你。这种现象即是典型的观察者模式。

    状态模式 (State)

           状态模式是允许对象在内部状态发生改变时改变它的行为。对象具有多种状态,且每种状态具有特定的行为。在网站的积分系统中,用户具有不同的积分,也就对应了不同的状态;还有QQ的用户状态有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态。

    策略模式 (Strategy)

           策略模式是定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换,且算法的变化不会影响到使用算法的客户。是为了统一接口下的一系列算法类(也就是多种策略),用一个类将其封装起来,使这些策略可动态切换。策略模式属于行为型模式,是为了使这些策略可以相互切换,是为了选择不同的行为。

    模板方法模式(Template Method)

           模板方法模式是定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。该模式就是在一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤,将一些固定步骤、固定逻辑的方法封装成模板方法。调用模板方法即可完成那些特定的步骤。

    访问者模式 (Visitor)

           访问者模式主要是将数据结构与数据操作分离。在被访问的类里面加一个对外提供接待访问者的接口,访问者封装了对被访问者结构的一些杂乱操作,解耦结构与算法,同时具有优秀的扩展性。通俗来讲就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。访问者模式将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。

    模式原则

    大家都开始注意设计模式。那么,到底我们为什么要用设计模式呢?这么多设计模式为什么要怎么设计呢?说实话,以前我还真没搞清楚。就是看大家一口一个"Design pattern",心就有点发虚。于是就买了本"四人帮"的设计模式,结果看得似懂非懂:看得时候好像是懂了,过一会就忘了。可能是本人比较"愚钝"吧:))最近,有了点感悟。"独乐不如众乐",与大家分享一下,还望指教! 为什么要提倡"Design Pattern"呢?根本原因是为了代码复用,增加可维护性。那么怎么才能实现代码复用呢?OO界有前辈的几个原则:"开-闭"原则(Open Closed Principal)、里氏代换原则、合成复用原则。设计模式就是实现了这些原则,从而达到了代码复用、增加可维护性的目的。

    开-闭原则

    此原则是由"Bertrand Meyer"提出的。原文是:"Software entities should be open for extension,but closed for modification"。就是说模块应对扩展开放,而对修改关闭。模块应尽量在不修改原(是"原",指原来的代码)代码的情况下进行扩展。那么怎么扩展呢?我们看工厂模式"factory pattern":假设中关村有一个卖盗版盘和毛片的小子,我们给他设计一"光盘销售管理软件"。我们应该先设计一"光盘"接口。而盗版盘和毛片是其子类。小子通过"DiscFactory"来管理这些光盘。代码为:

    public class DiscFactory{
        public static 光盘 getDisc(String name){
            return(光盘)Class.forName(name).getInstance();
        }
    }

    有人要买盗版盘,怎么实现呢?

    public class 小子{ 
        public static void main(String[] args){ 
            光盘 d=DiscFactory.getDisc("盗版盘"); 
            光盘.卖(); 
        } 
    }

    如果有一天,这小子良心发现了,开始卖正版软件。没关系,我们只要再创建一个"光盘"的子类"正版软件"就可以了。不需要修改原结构和代码。怎么样?对扩展开放,对修改关闭。"开-闭原则" 工厂模式是对具体产品进行扩展,有的项目可能需要更多的扩展性,要对这个"工厂"也进行扩展,那就成了"抽象工厂模式"。

    里氏代换原则

    里氏代换原则是由"Barbara Liskov"提出的。如果调用的是父类的话,那么换成子类也完全可以运行。比如: 光盘 d=new 盗版盘(); d.卖(); 要将"盗版盘"类改为"毛片"类,没问题,完全可以运行。Java编译程序会检查程序是否符合里氏代换原则。还记得java继承的一个原则吗?子类override方法的访问权限不能小于父类对应方法的访问权限。比如"光盘"中的方法"卖"访问权限是"public",那么"盗版盘"和"毛片"中的"卖"方法就不能是protected或private,编译不能通过。为什么要这样呢?你想啊:如果"盗版盘"的"卖"方法是private。那么下面这段代码就不能执行了: 光盘 d=new 盗版盘(); d.卖();可以说:里氏代换原则是继承复用的一个基础。

    合成复用原则

    就是说要少用继承,多用合成关系来实现。我曾经这样写过程序:有几个类要与数据库打交道,就写了一个数据库操作的类,然后别的跟数据库打交道的类都继承这个。结果后来,我修改了数据库操作类的一个方法,各个类都需要改动。"牵一发而动全身"!面向对象是要把波动限制在尽量小的范围

    在Java中,应尽量针对Interface编程,而非实现类。这样,更换子类不会影响调用它方法的代码。要让各个类尽可能少的跟别人联系,"不要与陌生人说话"。这样,城门失火,才不至于殃及池鱼。扩展性和维护性才能提高

    理解了这些原则,再看设计模式,只是在具体问题上怎么实现这些原则而已。张无忌太极拳,忘记了所有招式,打倒了"玄幂二老",所谓"心中无招"。设计模式可谓招数,如果先学通了各种模式,又忘掉了所有模式而随心所欲,可谓OO之最高境界。

    依赖倒转原则

    要针对接口编程,而不是针对实现编程。传递参数,或者在组合聚合关系中,尽量引用层次高的类。主要是在构造对象时可以动态的创建各种具体对象,当然如果一些具体类比较稳定,就不必在弄一个抽象类做它的父类,这样有画舌添足的感觉。

    接口隔离原则

    一种角色,不多不少,不干不该干的事,该干的事都要干

    抽象类

    抽象类抽象类不会有实例,类为子类继承,一般包含这个类的共同属性和方法。注意:好的继承关系中,只有叶节点是具体类,其他节点应该都是抽象类,也就是说具体类是不被继承的。将尽可能多的共同代码放到抽象类中。 

    展开全文
  • 【《软件设计模式与体系结构》学习笔记】软件设计模式的概念软件设计模式是对软件设计经验的总结,是对软件设计中反复出现的设计问题的已被验证的成功解决之道。大量的软件设计模式都是之前从事软件设计开发的前人...

    【《软件设计模式与体系结构》学习笔记】

    软件设计模式的概念


    软件设计模式是对软件设计经验的总结,是对软件设计中反复出现的设计问题的已被验证的成功解决之道。大量的软件设计模式都是之前从事软件设计开发的前人经过大量的实践而摸索出来的,用于帮助后来者快速高效且高质从事软件开发的。

    软件设计模式的要素


    软件设计模式一般会包含四个基本要素:

    • 模式名称:此种设计模式的名字;
    • 问题:是设计者所面临的设计场景,也就是此种设计模式所适用的情况;
    • 解决方案:描述设计细节,通常会采取UML等图示的方式来进行设计模式的详细描述;
    • 效果:描述适用此设计模式的优势与劣势,包括面向软件的质量属性等。

    软件设计模式的分层


    软件设计模式根据问题的规模可以分为三个层次
    架构模式 -> 设计模式 -> 习惯用法

    1. 架构模式:描述系统级的结构组成、相互关系及相关约束,如MVC模式;
    2. 设计模式:针对系统局部设计问题给出的解决方案,一般情况下,设计模式指的就是这一层次的;
    3. 习惯用法:与具体编程语言相关的一种底层模式。

    软件设计模式的分类


    《软件设计模式与体系结构》一书中将设计模式归类如下:

    面向对象分布式计算企业应用软件面向服务的体系结构(SOA)
    创建型模式从混沌到结构领域逻辑模式服务设计模式
    结构型模式分布式基础设施数据源结构模式服务库设计模式
    行为型模式事件分离与分发对象——关系行为模式服务组合设计模式
    接口划分对象——关系结构模式
    组件划分对象——关系元数据映射模式
    应用控制Web表现模式
    并发分布模式
    同步离线并发模式
    对象交互会话状态模式
    适配与扩展基本模式
    模态行为
    资源管理
    数据库访问

    感悟

    在我们日常学习中,有些时候不知不觉的应用到某些设计模式,但我们很难意识到这可以抽象为一种思想方法,并且是可以被他人当为一种模式的设计方法。所以,在以后我们又碰到类似问题时,又会重新将以前的思路再来一次,等到脑中的设计思想快成型的时候,才会恍然大悟,一拍脑门道:“哦,这个东西我好像上一次做过。”

    设计模式是前人经过验证的成功的解决方案,我们应该要善于学习,学会运用,别辜负了前辈们的心血。站在巨人的肩膀上,我们会看得更远。

    展开全文
  • 嵌入式软件设计设计模式

    千次阅读 2019-04-21 17:24:31
    文章目录前言1.设计模式之适配器模式2.设计模式之单例模式3.设计模式之命令模式 前言 在嵌入式软件设计过程中,...适配器模式是一种比较常用软件设计模式(有时候叫做包装模式)。它将对象的接口转换成对于客户端(...
  • 面试——常用设计模式

    万次阅读 多人点赞 2018-08-31 16:56:26
    一、软件设计模式的几种分类: 1.1.创建型 创建对象时,不再由我们直接实例化对象;而是根据特定场景,由程序来确定创建对象的方式,从而保证更大的性能、更好的架构优势。创建型模式主要有简单工厂模式(并不是23...
  • 软件设计模式大作业

    热门讨论 2013-03-04 22:27:09
    java设计模式期末大作业,运用了6种模式,包括简单工厂模式、工厂方法模式、单例模式、门面模式、策略模式、观察者模式,文档包括系统流程,系统类图,各个模式的子类图,源代码,实验截图。绝对完整.
  • Java软件设计模式精讲

    2020-05-28 16:42:55
    而这些都源于一个问题,那就是软件设计没做好。这门课能帮助你很好的认识设计模式,让你的能力得到提升。 课程大纲: 为了让大家快速系统了解设计模式知识全貌,我为您总结了思维导图,帮您梳理学习重点,建议收藏!
  • Android中常用设计模式

    千次阅读 2018-05-04 14:41:04
    Android开发中常见的设计模式Android中常见的设计模式Android设计模式之23种设计模式一览Android中常用设计模式二:然后总结一下其总结内容多根据参考文献文档而来,还请了解文档内容(一)什么是设计模式1....
  • 设计模式-结构型软件设计模式(二)

    千次阅读 2017-07-13 20:07:02
    适配器模式介绍在软件设计中,为了解决接口不一致的问题,两个软件模块之间往往也需要通过一个适配器类Adapter进行是适配。这样的模式叫做适配器设计模式。 适配器模式分为两种,一种是类适配器,一种是对象适配器...
  • 设计模式-创建型软件设计模式(一)

    千次阅读 2017-07-13 14:30:57
    主要介绍下面的三个设计模式: (1)工厂模式与抽象工厂模式 (2)生成器模式 (3)单例模式工厂模式工厂模式可以分为简单工厂模式,工厂模式以及抽象工厂模式。简单工厂模式简单工厂模式的特点是仅仅有一个具体...
  • 什么是软件设计模式

    千次阅读 2016-04-17 15:38:31
    设计模式(Design pattern)是一套被反复使用、多数人知晓的、...设计模式软件工程的基石脉络,如同大厦的结构一样。一 、设计模式分为三种类型: 1.创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原
  • 常用的JavaScript设计模式

    万次阅读 2018-03-24 11:39:58
    什么是设计模式百度百科: 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过...设计模式软件工程的基石脉络,如同大厦的结构一样。 实际情况: 设计模式绝对不是纸上谈兵的知识,光看书就以为...
  • 设计模式-结构型软件设计模式(四)

    千次阅读 2017-07-14 12:38:13
    桥接模式将一个软件设计的抽象部分与实现部分分离,使它们都可以独立地变化。 (1)abstraction接口:定义抽象部分的接口,维持Implementor对象的一个参考(Reference)。 (2)RefinedAbstraction类:是一个实类...
  • 一、软件设计模式的产生背景 “设计模式”这个术语最初并不是出现在软件设计中,而是被用于建筑领域的设计中。 1977 年,美国著名建筑大师、加利福尼亚大学伯克利分校环境结构中心主任克里斯托夫·亚历山大...
  • Java常用设计模式

    万次阅读 多人点赞 2019-03-10 17:51:04
    设计模式是编程解决实际问题或类似问题的最佳实践,Java编程中处处都是对象,对象需要创建,对象间需要相互调用,对象的模板是类,类与类需要相互关联引用,如何通用解决上述问题,经过长时间的编程实践总结出来的...
  • Java常见设计模式总结

    万次阅读 多人点赞 2021-09-18 17:18:54
    设计模式于己于人于系统都是多赢的,它使得代码编写真正工程化,它是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种...
  • 设计模式-结构型软件设计模式(三)

    千次阅读 2017-07-14 12:37:45
    外观模式简介外观模式用来隐藏一个软件系统的所有内部细节,只提供给客户类一个外观类,或者叫做接口类。客户类直接调用该外观类的方法即可,而不必担心这些方法对其他类的调用的内部细节。外观模式角色(1)外观...
  • 软件秘笈-设计模式那点事.pdf

    千次下载 热门讨论 2013-10-10 09:20:54
    本书在第1章软件设计模式概述后,从第2章到第24章诠释23个软件设计模式。每一种都以一个生活故事开始,然后是模式定义、模式分析、模式实现、设计原则和使用场合。模式实现通过Eclipse中的Java工程展开,采用软件...
  • Spring中常用设计模式 我们通常说的23种经典设计模式可以通过下表一目了然: 通常来说,设计模式都是混合使用,不会独立应用。利用穷举法充分理解设计模式的应用场景。在平时的应用中,不是用设计模式去生搬硬套...
  • 常用设计模式

    万次阅读 2009-01-28 03:25:00
    常用设计模式根据我的经验我把我经常用到的设计模式在这里做个总结,按照我的经验,它们的排序如下:1)单件模式、2)抽象工厂模式和工厂模式、3)适配器模式、4)装饰模式、5)观察者模式、6)外观模式 其他模式目前...
  • 设计模式之单例设计模式

    万次阅读 多人点赞 2021-03-21 20:28:27
    设计模式软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。 JAVA一共有23种设计模式,我们今天首先来学其中一种:单例设计模式 ...
  • 软件设计模式深入理解

    千次阅读 2020-09-16 13:42:43
    设计模式1、设计模式基本要素4个主要部分2、按照目的分类 1、设计模式基本要素4个主要部分 该模式名称使用什么解决方案去解决了什么问题会产生什么样的效果 2、按照目的分类
  • 23种设计模式常用模式

    千次阅读 多人点赞 2018-08-08 09:14:41
    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段...
  • 设计模式-结构型软件设计模式(一)

    千次阅读 2017-07-13 18:58:01
    组合模式简介组合模式允许一致的对待复杂和原始对象的接口,在面向对象编程技术中,组合对象是一个或者多个相似对象构成的对象,各个对象有相似的功能。关键的概念是客户类以相同的方式对待单独的对象与一组对象,即...
  • 设计模式-创建型软件设计模式(三)

    千次阅读 2017-07-13 17:04:22
    单例模式简介单例模式是指确保一个类仅有一个唯一的实例,并且提供一个全局的访问点。思路实现单例模式的思路:将构造方法声明为private。并且提供一个可以获得实例的方法,该方法必须是静态方法,并且确保无论客户...
  • 设计模式-行为型软件设计模式(五)

    千次阅读 2017-07-15 00:05:42
    策略模式介绍策略模式定义了一系列的算法,将每一个算法封装起来,并且使它们之间可以相互替换,策略模式让算法的变化不会影响到使用算法的客户。 (1)Stragegy:定义了一个共同的接口。所有具体的算法类实现这...
  • 设计模式-行为型软件设计模式(一)

    千次阅读 2017-07-14 12:39:16
    迭代器模式迭代器模式的关键思想是将对列表的访问和遍历从列表对象中分离出来,放入一个独立的迭代器当中。迭代器类定义了一个访问该列表元素的接口。迭代器类所提供的方法负责跟踪当前的元素,即它知道哪些元素已经...
  • 设计模式-行为型软件设计模式(六)

    千次阅读 2017-07-15 00:05:59
    状态模式介绍状态模式将不同状态下的行为封装在不同的类中,每个类代表一个状态。 (1)Context:定义了与客户程序的接口,它保持了一个ConcreteState的代表现在状态的实例。 (2)State:定义了状态接口,它的...
  • 史上最全设计模式导学目录(完整版)

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

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 716,502
精华内容 286,600
关键字:

常用的软件设计模式