java23种设计模式详解
2016-02-20 18:49
603 查看
跟晓明一起学java之java设计模式
本文参考了:http://my.oschina.net/tiancai/blog/224624一、设计模式的分类
1、创建型模式有五种:工厂方法模式,抽象工厂模式,单例模式,创建者模式,原型模式。
2、结构型模式
有七种:适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
3、行为型模式
有十一种:策略模式,模板方法模式,责任链模式,命令模式,迭代子模式,观察者模式,备忘录模式,状态模式,
中介者模式,访问者模式,解释器模式。
二、设计模式的基本原则
待完成三、设计模式详解
1、创建型模式
首先介绍一下一下简单工厂模式,简单工厂模式不属于23种设计模式之一。简单工厂模式
(1)单方法
简单工厂模式就是创建一个工厂类,对实现了同一个接口的实现类进行实例化。
<1> 关系图
<2>首次创建接口:
public interface ISend { public void send(); }
<3> 然后创建实现类:
/** * 邮件发送实现类 * @author Administrator * */ public class MailSend implements ISend{ @Override public void send() { System.out.println("邮件发送信息!"); } }
/** *短信发送实现类 * @author Administrator * */ public class MessageSend implements ISend{ @Override public void send() { System.out.println("短信发送信息!"); } }
<4>创建简单工厂类:
/** * 简单工程类 * @author Administrator * */ public class SigleMethodFactory { public ISend produce(String type){ if("mail".equals(type)){ return new MailSend(); }else if("message".equals(type)){ return new MessageSend(); } return null; } }<5>测试
public class SimpleFactoryTest { public static void main(String[] args){ ISend send = new SigleMethodFactory().produce("mail"); send.send(); } }输出:邮件发送信息!
(2)多方法
是对单方法简单工厂方法模式的改进,如果但方法传入的字符串不正确,则不能创建出对象,而多个方法的简单工厂模式,是提供多个工厂方法来创建不同的对象。
<1>关系图
<2>接口及实现类同单方法的简单工厂。
<3>工厂类:
public class MutiMethodSimpleFatory { public ISend produceMessage(){ return new MessageSend(); } public ISend produceMail(){ return new MailSend(); } }<4>测试
public class MutilMethodSmpFactoryTest { public static void main(String[] args) { ISend send = new MutiMethodSimpleFatory().produceMessage(); send.send(); } }输出:短信发送信息!
(3)静态方法
静态方法的简单工厂模式就是把工厂类里面的方法都修改为静态方法,直接调用,无需手动实例化简单工厂类。
总结:简单工厂模式适用于需要有大量的类需要实例化,而且这些类具有共同的接口,在以上三种中,第一种情况,如果传入的字符串错误,则不能正确创建对象,
第三种情况相对于第二种情况来讲,不需要实例化工厂类。所以大多数情况下使用第三种方式。
1.1 工厂方法模式(Factory Method)
简单工厂模式,类的创建依赖工厂类,如果要实现类的扩展,就必须修改工厂类,所以违反了设计模式的对修改关闭,对扩展开放的设计原则,这个这个问题使用工厂方法模式就可以解决,创建一个工厂接口类和创建多个工厂的实现类,如果需要增加新的功能,直接增加工厂类即可解决。
<1>关系图
<2>发送的接口及实现类同简单工厂模式
<3>创建工厂类接口
public interface IFactoryMethod { public ISend produce(); }<4>创建工厂类实现类
public class MailFatory implements IFactoryMethod{ @Override public ISend produce(){ return new MailSend(); } }
public class MessageFacory implements IFactoryMethod { @Override public ISend produce() { return new MessageSend(); } }<5>测试
public class FactoryMethodTest { public static void main(String[] args){ IFactoryMethod factory = new MailFatory(); ISend send = factory.produce(); send.send(); } }输出:短信发送信息!
1.2 抽象工厂模式(Abstact Factory)
工厂方法与抽象工厂的区别:工厂方法模式:
一个抽象产品类,可以派生出多个具体的产品类。
一个抽象工厂类,可以派生出多个具体的工厂类。
抽象工厂模式:
多个抽象产品类,每个抽象产品都可以派生出多个具体的实现类。
一个抽象工厂类,可以派生出多个具体的工厂类。
区别:
工厂方法只有一个抽象产品,抽象工厂有多个。
工厂方法的具体实现类只能创建一个类的实例,而抽象工厂可以创建多个。
工厂方法侧重于怎么创建,抽象工厂侧重于创建那些。
比如利用抽象工厂实现对数据库访问的不同实现(jdbc,hibernate)等。
<1>关系图
<2>工厂接口类
public interface IAbstractFactory { public IMouse createMouse(); public IKeyBoard createKeyBoard(); }<3>工厂接口的实现类
public class IntellFactory implements IAbstractFactory{ @Override public IMouse createMouse() { return new IntellMouse(); } @Override public IKeyBoard createKeyBoard() { return new IntellKeyBoard(); } }
public class LenevoFactory implements IAbstractFactory { @Override public IMouse createMouse() { return new LenevoMouse(); } @Override public IKeyBoard createKeyBoard() { return new LenevoKeyBoard(); } }<4>实体类接口
public interface IMouse { public void click(); }
public interface IKeyBoard { public void hit(); }<5>实体类接口实现类
public class LenevoMouse implements IMouse{ @Override public void click() { System.out.println("lenevo mouse"); } }
public class IntellMouse implements IMouse{ @Override public void click() { System.out.println("intell mouse"); } }
public class LenevoKeyBoard implements IKeyBoard{ @Override public void hit() { System.out.println("lenevo keyBoard"); } }
public class IntellKeyBoard implements IKeyBoard { @Override public void hit() { System.out.println("intell keyBoard"); } }<5>测试
public class AbstractFatoryTest { public static void main(String[] args) { IAbstractFactory factory = new IntellFactory(); IKeyBoard keyBoard = factory.createKeyBoard(); IMouse mouse = factory.createMouse(); keyBoard.hit(); mouse.click(); } }输出:
intell keyBoard
intell mouse
1.3 单例模式(Sigleton)
单例模式的特点:单例类必须只有一个实例;
单例类必须自己创建自己的实例;
单例类必须给其他所有实例提供这一个对象。
单例模式就是要保证某个类只有一个实例,并且自行实例化并向系统提供这个实例。在计算及系统中,线程池,日志,
缓存,打印机等常被设计成单例类。
<1>懒汉式单例
/** * 线程不安全的 * @author Administrator * */ public class LazySingleton { //私有的静态变量,防止被引用 private static LazySingleton instance = null; //私有的构造器,防止被实例化 private LazySingleton(){ } private static LazySingleton getInstance(){ if(instance == null){ instance = new LazySingleton(); } return instance; } }
/** * 线程安全的懒汉式 * @author Administrator * */ public class SafeLazySingleton { private static SafeLazySingleton instance = null; private static synchronized SafeLazySingleton init(){ if(instance == null){ return new SafeLazySingleton(); } return instance; } public static SafeLazySingleton getInstance(){ if(instance == null){ init(); } return instance; } }
<2>饿汉式单例
public class HungrySingle { private static HungrySingle instance = new HungrySingle(); private HungrySingle(){ } public static HungrySingle getInstance(){ return instance; } }
1.4 建造者模式(Builder)
工厂模式提供的是创建单个类的模式,建造者模式将多个产品集中起来进行管理,用来创建集合对象。public class Builder { public List<ISend> list = new ArrayList<ISend>(); public void produceMailSend(int count){ for(int i = 0;i < count;i++){ list.add(new MailSend()); } } public void produceMessageSend(int count){ for(int i = 0;i < count;i++){ list.add(new MessageSend()); } } }从这点可以看出,创建者模式是将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。
所以与工程模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建复合对象。
具体选择哪一种模式来创建对象需要依据具体的情况来定。
1.5 原型模式(ProtoType)
原形模式,就是讲一个对象作为原型,对其进行复制,克隆,产生一个与原来对象相类似的新对象。一个原型类只需要实现cloneable接口,覆盖clone方法(),super.clone()调用object的clone方法完成复制。
浅复制:将一个对象复制后,基本类型的变量会被重新创建,而引用类型,指向的还是原对象所指向的。
深复制:无论基本类型的变量还是引用类型的变量,都会重新的创建一份。
public class ProtoType implements Cloneable{ //浅复制 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(); } }所以说,深复制需要采用流的方式读入当前对象的二进制输入,然后在写出二进制数据到新的对象。
2、结构型模式
2.1 适配器模式(Adapter)
适配器模式是将某个类的接口转化成客户端需要的另外一个接口,目的是消除接口不匹配所造成的类的兼容性问题。主要分为3类,类的适配器模式,对象的适配器模式,接口的适配器模式。
2.1.1 类的适配器
<1>关系图核心思想,有一个Source类,拥有一个方法,待适配,目标接口是ITarget ,通过ClassAdapter将Source的功能扩展到ITarget.
<2>待适配的类
public class Source { public void method1(){ System.out.println("Source method1"); } }<3>目标接口
public interface ITarget { public void method1(); public void method2(); }<4>类适配器
public class ClassAdapter extends Source implements ITarget{ @Override public void method2() { System.out.println("ClassAdapter method2"); } }<5>测试
public class ClassAdapterTest { public static void main(String[] args){ ITarget target = new ClassAdapter(); target.method1(); target.method2(); } }输出:
Source method1
ClassAdapter method2
这样ITarget接口就具备了Source类的功能。
2.1.2 对象的适配器
基本思路与类的适配器相同,但是这次不继承待适配的类(Source),而是持有待适配器类的对象(Source)。<1>关系图
<2>需要对适配器类进行调整
public class ObjectAdapter implements ITarget{ Source source = null; public ObjectAdapter(Source source){ this.source = source; } @Override public void method1() { source.method1(); } @Override public void method2() { System.out.println("ObjectAdapter method2"); } }
2.1.3 接口适配器模式
有些时候,我们写一个接口,里面会有多个抽象方法,当我们写接口的实现类的时候,必须要实现这个接口的所有方法,这明显比较浪费,因为并不是所有的方法都是我们所需要的,有时需要一些就够了,此处为了解决这个问题,引入了接口的适配器模式,借助一个抽象类,该
抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重
写我们需要的方法就行。
<1>关系图
<2>适配器类
public abstract class InterfaceAdapter implements ITarget{ @Override public void method1() { } @Override public void method2() { } }<3>具体实现类
public class Sub1 extends InterfaceAdapter{ @Override public void method1() { System.out.println("Sub1 method1"); } }
public class Sub2 extends InterfaceAdapter { @Override public void method2() { System.out.println("Sub2 method2"); } }总结:
类的适配器:希望将一个类转化成满足另外一个接口的类时,可以使用类的适配器模式,创建一个类,继承待适配类并实现接口接口。
对象适配器:当希望把一个对象转换成满足另一个新接口的对象是,可以创建一个类,持有待适配的类的对象,在创建的类中,调用待适配的类的对象的方法即可。
接口适配器:当不希望实现一个接口的所有方法时,可以创建一个抽象类,实现所有方法,我们写别的类时,直接继承抽象类即可。
2.2装饰器模式(Decorator)
装饰器模式,是为了给一个对象增加新的功能,而且是动态的,要求装饰的对象和被装饰的对象实现同一个接口,装饰对象持有被装饰对象的实例。<1>关系图
<2>接口
public interface DecoraterInterface { public void method1(); }<3>被装饰类
public class Source implements DecoraterInterface { @Override public void method1() { System.out.println("Source method1"); } }<4>装饰类
public class Decorator implements DecoraterInterface{ Source source = null; public Decorator(Source source){ this.source = source; } @Override public void method1() { System.out.println("前装饰"); source.method1(); System.out.println("后装饰"); } }装饰器的应用场景:
1、需要扩展一个类的功能。
2、动态增加一个类的功能,继承只是静态的。
缺点是产生过多相似的类,不容易拍错。
2.3 代理模式
代理模式就是多一个代理类出来,替换原来对象进行一些操作,比如我们租房子需要找中介,打官司需要找律师。<1>关系图
<2>接口
public interface IProxyInterface { public void method(); }<3>被代理类
public class Source implements DecoraterInterface { @Override public void method1() { System.out.println("Source method1"); } }<4>代理类
public class Proxy implements IProxyInterface{ Source source = null; public Proxy(){ source = new Source(); } @Override public void method() { System.out.println("代理前"); source.method(); System.out.println("代理后"); } }<5>测试类
public class ProxyTest { public static void main(String[] args) { IProxyInterface proxy = new Proxy(); proxy.method(); } }输出:
代理前
Source method!
代理后
总结:代理模式与装饰器模式的区别
代理模式关注的是如何动态给类添加功能,代理模式关注的是控制对象的访问。
用代理模式,代理类会对它的客户隐藏具体的对象信息,因此,当我们使用代理类的时候,一般在代理类里面创建一个对象。
用装饰器模式,对象一般作为一个原始参数传递给装饰者的构造器。
2.4 外观模式(Facade)
外观模式,是为了解决类与类之间的依赖关系,有将spring,将类与类之间的关系在配置文件中进行管理,外观模式是将类与类之间的关系放置在一个Facade类中.降低了类与类之间的耦合程度,该模式没有涉及到接口。
<1>关系图
<2>实体类
public class Cpu { public void start(){ System.out.println("Cpu start"); } public void shutdown(){ System.out.println("cpu shutdown"); } }
public class Memory { public void start(){ System.out.println("Memory start"); } public void shutdown(){ System.out.println("Memory shutdown"); } }<3>外观类
public class ComputerFacade { private Memory memory = null; private Cpu cpu = null; public ComputerFacade(){ memory = new Memory(); cpu = new Cpu(); } public void startup(){ memory.start(); cpu.start(); } public void shutdown(){ memory.shutdown(); memory.shutdown(); } }<strong> </strong><4>测试类
public class FacadeTest { public static void main(String[] args) { ComputerFacade computer = new ComputerFacade(); computer.startup(); computer.shutdown(); } }输出:
Memory start
Cpu start
Memory shutdown
Memory shutdown
总结:如果没有ComputerFacade类,则Memory与Cpu类则相互依赖,修改一个类,就会导致另外类的修改。
有了ComputerFacade类,他们之间的关系被放在了ComputerFacade类里面,这样就起了解耦的作用,这就是外观模式。
2.5 桥接模式(Bridge)
桥接模式就是把事物和其具体的类分开,使他们各自独立变化。桥接的用意是,将抽象化与实现化解耦,使得二者可以独立变化,向我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要
动太多代码,甚至丝毫不用动,原因是因为JDBC提供了统一的接口,每个数据库各自的实现,用一个叫数据库驱动的程序
来桥接就行了。
<1>关系图
<2>定义接口
public interface SourceAble { public void method(); }<3>定义实现类
public class Sub1 implements SourceAble { @Override public void method() { System.out.println("Sub1 method!"); } }
public class Sub2 implements SourceAble { @Override public void method() { System.out.println("Sub2 method!"); } }
<4>定义桥接类 持有SourceAble的实例
public abstract class Bridge { SourceAble source = null; 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(); } }<5>测试类
public class BridgeTest { public static void main(String[] args) { SourceAble source = new Sub1(); Bridge bridge = new MyBridge(); bridge.setSource(source); bridge.method(); } }输出:
Sub1 method!
这样通过对Bridge的调用,实现了对接口SourceAble的实现类SourceSub1和SourceSub2的调用。接下来在画一个图,是JDBC的连接原理,
有数据库学习基础的,一结合都懂了。
2.6 组合模式(Composite)
组合模式有时又叫部分-整体模式,在处理类似树形结构的问题时比较方便。<1>关系图
<2>实体类
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!"); } }使用场景:将多个对象组合在一起进行操作,常用于表示树结构,录入二叉树等。
2.7 享元模式(Flyweight)
享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销。通常与工厂模式一起使用。<1>关系图
FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,
就返回已经存在的对象,FlyWeight是超类。一提到共享池,我们很容易联想到java里面的JDBC连接池,想想每个连接的特点,我们
不难总结出:适合于做共享的一些个对象,他们有一些共有的属性,就拿数据库连接池来说,url、driverClassName、username、
password及dbname,这些属性在每个连接说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部
数据,其他的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。
看个例子:
<2>代码
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; } } }<strong> </strong>通过连接池的管理,实现了数据库链接的共享,不需要每一次都从新链接,节省了数据库重新创建的开销,提升了系统的性能。
3.行为型模式
第一类:父类与子类 策略模式,模板方法模式第二类:类与类之间 观察者模式,命令模式,责任链模式,迭代子模式
第三类:类的状态 备忘录模式,状态模式
第四类:通过中间类 访问者模式,中介者模式,解释器模式
3.1 策略模式
策略模式就是定义一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化的不会影响到使用他的客户。需要设计一个接口,为一系列的实现类提供统一的方法,多个实现类事项该接口,设计一个抽象类(可有可无),提供辅助函数。
<1>关系图
<2>代码
待完成
总结:策略模式的决定权在客户,系统本身提供不同的算法去实现。
3.2 模板方法模式(Template method)
一个抽象类中,有一个主方法,在定义1..n个方法,可以是抽象的,也可以是实际的。定义一个类继承该抽象类,重新抽象方法,通过调用抽象类,实现对子类的调用。
<1>关系图
3.3观察者模式
就像我们订阅邮件或文章一样,如果有更新,会及时同时我们,如果一个对象有变化,其他依赖该对象的对象都会产生变化。<1>关系图
3.4 迭代子模式(Interator)
顾名思义,迭代器模式就是顺序访问聚集中的对象.
<1>关系图
3.5 责任链模式(Chain of Responsibility)
责任链模式,有多个对象,每个对象都持有下一个对象的引用,这样就会形成一条链,请求在这条链上传递,值到某一个对象决定处理该请求。
<1>关系图
AbstractHandle提供了get set方法,方便对象的修改,MyHandel是核心,实例化后生成一系列相互持有的对象。
3.6 命令模式(Commond)
例如司令员让士兵去干一件事情,从整个事情的角度来讲,司令员的作用是发送口令,口令经过传递,传到了士兵的耳朵,士兵去执行。二者互相解耦,任何一方都不用去依赖其他人,司令员要的是结果,不会去关注士兵
是怎么实现的。
<1>关系图
3.7 备忘录模式
3.8 状态模式
3.9 中介者模式
3.10 访问者模式
3.11 解释器模式
相关文章推荐
- Spring AOP
- JAVA:Random的种子含义
- mybatis0211 mybatis和spring整合
- ubuntu安装java eclipse
- java(16)--利用session存储购买商品
- spring-aop学习笔记
- java中transient关键字
- JAVA多线程-线程间通信(三)-通过管道进行线程间通信
- How to improve Java's I/O performance( 提升 java i/o 性能)
- eclipse在异常关闭后,导致无法启动workspace问题的解决
- 关于$.ajaxFileUpload()上传图片
- 解决Java接口内部类的main()方法无法打印输出的问题
- java反射学习
- Eclipse配置PyDev插件
- Java 序列化Serializable详解(附详细例子)
- Spring 多数据源事务配置问题
- Java集合类的区别
- 安卓开发工具Eclipse、SDK等升级攻略
- Java enum(枚举)的用法详解(转)
- freemaker-eclipse-plugin 安装