首页 > 代码库 > 设计模式之创建型模式(5种)
设计模式之创建型模式(5种)
工厂方法模式和抽象工厂模式
简单工厂模式
通常方法都是静态的,所以也被称作静态工厂
虽然从理论上,简单工厂什么都能创造,但是对于简单工厂可创建对象的范围来说,通常不要太大,建议将其控制在一个独立组件级别或者一个模块级别,也就是一个组件或模块对应一个简单工厂
类名建议为"模块名称+Factory",如"UserFacory";方法名通常为"get+接口名称"或者"create+接口名称",如"getUserBean"
public interface Player{ //运动员接口 public viod run(); public void jump(); } public class FootballPlayer implements Player{ //足球运动员 public viod run(){ //do something }; public viod jump(){ //do something }; } public class BasketballPlayer implements Player{ //篮球运动员 public viod run(){ //do something }; public viod jump(){ //do something }; } public class PlayerFactory{ //球员工厂类 public static Player get FootballPlayer (){ return new FootballPlayer(); } public static Player get BasketballPlayer(){ return new BasketballPlayer (); } } public class Test{ //足球俱乐部(篮球俱乐部与此类似) private Player goalkeeper; //守门员 private Player forward; //前锋 private Player defender; //后卫 public void clubTest(){ this. goalkeeper = PlayerFactory. FootballPlayer(); this. forward = PlayerFactory. FootballPlayer(); this. defender = PlayerFactory. FootballPlayer(); } }简单工厂解决了不用接口、不用工厂而把具体类暴露给客户端时的混乱情形,接口就是用来封装和隔离具体实现的,目标就是不要让客户端知道封装内部的具体实现
简单工厂的本质目的就是:选择一个合适的实现类来使用
工厂方法模式
工厂方法模式和简单工厂模式的区别在于:简单工厂模式只有一个工厂类,而工厂方法模式有一组实现了相同接口的工厂类
public interface Player{ //运动员接口 public viod run(); public void jump(); } public class FootballPlayer implements Player{ //足球运动员 public viod run(){ //do something }; public viod jump(){ //do something }; } public class BasketballPlayer implements Player{ //篮球运动员 public viod run(){ //do something }; public viod jump(){ //do something }; } public interface Club{ //俱乐部的接口类 public Player register(); } public class FootballClub implements Club{ //足球俱乐部的具体工厂类 public Player register(){ return new FootballPlayer(); }; } public class BasketballClub implements Club{ //篮球俱乐部的具体工厂类 public Player register(){ return new BasketballPlayer (); }; } public class Test{ //足球俱乐部(篮球俱乐部与此类似) private Player goalkeeper; //守门员 private Player forward; //前锋 private Player defender; //后卫 Club club = new FootballClub(); public void clubTest (){ this. goalkeeper = club.register(); this. forward = club .register(); this. defender = club .register(); } }
抽象工厂模式抽象工厂模式和工厂方法模式最大的区别在于需要创建对象的复杂程度上,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构
抽象工厂的目的是给客户端提供一个接口,可以创建多个产品族中的产品对象,使用抽象工厂还要满足以下条件:
1、系统中有多个产品族,而系统一次只能消费其中一族产品;
2、同属于同一个产品族的产品一起使用
在下例中,足球队的足球运动员和足球教练就是一个产品族,同理,篮球队一样。足球教练不能训练篮球队,篮球教练也不能训练足球队,他们是和运动员的类别捆绑在一起的
public interface Player{ //运动员接口 public viod run(); public void jump(); } public class FootballPlayer implements Player{ //足球运动员 public viod run(){ //do something }; public viod jump(){ //do something }; } public class BasketballPlayer implements Player{ //篮球运动员 public viod run(){ //do something }; public viod jump(){ //do something }; } pubic interface Coach{ //教练接口 public void train(); } public class FootballCoach implements Coach{ //足球教练 public void train(){ //足球教练带领足球运动员训练 }; } public class FootballCoach implements Coach{ //篮球教练 public void train(){ //篮球教练带领篮球运动员训练 }; } public interface AbstractClubFactory{ //俱乐部的抽象工厂 public Coach createCoach(); //聘请教练 public Player createPlayer(); //招纳运动员 } public class FootballFactory implements AbstractClubFactory{ //足球队工厂 public Coach createCoach(){ //聘请足球教练 return new FootballCoach(); }; public Player createPlayer(){ //招纳足球运行员 return new FootballPlayer (); }; } public class FootballFactoryimplements AbstractClubFactory{ //篮球队工厂 public Coach createCoach(){ //聘请篮球教练 return new FootballCoach(); }; public Player createPlayer(){ //招纳篮球运行员 return new FootballPlayer (); }; } public class Test{ //测试类 private Coach coach; private Player player; public void createClub(AbstractClubFactoryfactory){ coach = factory. createCoach(); player = factory. createPlayer(); } }
测试类中,createClub(AbstractClubFactoryfactory)方法接受一个实现了AbstractClubFactory接口的类作为参数,然后就可以创建对应的球队了,如传进去的是FootballFactory,则会创建出足球教练和足球运动员,如果为FootballFactory,则会创建篮球运动员和篮球教练
假如现在要增加一个产品族——排球队,同样需要教练和运动员,需要从三处来考虑,首先需要增加一个实现了了Player接口的排球运行员类,然后需要增加一个实现了Coach接口的排球教练类,最后还需要一个实现了AbstractClubFactory、可以创建出排球教练和排球运动员的工厂类。可见,抽象工厂可以很好地支持开闭原则,即当需求放生改变时,是通过增加代码实现的,而不是对原有代码进行改动
单例模式
单例模式具有以下特点:
1、该类只有一个实例
2、该类自行创建实例,在该类内部创建自身的实例对象
3、向整个系统公开这个实例接口
对于单例模式,不管采用何种实现方式,它都是只关心类实例的创建问题,并不关心具体的业务逻辑
在Java里面实现的单例范围(即在什么范围内保证只有一个实例),是一个ClassLoader及其子ClassLoader的范围,如果一个虚拟机里面有多个ClassLoader,而这些ClassLoader都装载这个类的话,就算这个类是单例模式,它也会产生多个实例
一般建议单例模式的方法命名为getInstance
单例模式分为三种:懒汉式单例、饿汉式单例和登记式单例
懒汉式单例
也成为延迟加载,在类加载的时候并不创建实例,只有在第一次请求实例的时候才去创建,并且只创建一次
public class Singleton{ private static Singleton uniqueInstance =null; private Singleton(){ //私有化构造方法,外部不能直接创建 } //加上synchronized关键字,是为了防止在多线程环境下,多个线程同时调用getInstance会出错 public static synchronized SingletongetInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }饿汉式单例
在类加载的时候,唯一实例已经被创建
public class Singleton{ private static Singleton uniqueInstance =new Singleton(); //可以加上final关键字,防止外部修改 private Singleton(){ //私有化构造方法,外部不能直接创建 } public static Singleton getInstance() { return instance; } }登记式单例
public class Singleton{ //登记簿,用来存放所有登记的实例 private static Map<String,Singleton>registry = new HashMap(); static{ //类加载的时候添加一个实例到登记簿 Singleton sing = new Singleton(); registry.put(sing.getClass().getName(),sing); } protected Singleton(){ //受保护的构造方法 } //对于已登记的直接取出;还没登记的,先登记,再取出 public static Singleton getInstance(Stringname) { if(name == null){ name = "Singleton"; } if(registry.get(name) ==null){ try{ registry.put(name,(Singleton)Class.froName(name).newInstance); }catch(Exception e){ e.printstackTrace(); } returnregistry.get(name); } }
例如,系统在很多地方都需要使用配置文件的内容,就可以考虑使用单例模式来节省内存资源
public class AppConfig { private static AppConfig instance = newAppConfig(); private AppConfig(){ //私有化构造方法 readConfig(); } public static AppConfig getInstance(){ return instance; } private String parameteA; private String parameteB; public String getParameterA(){ return parameteA; } public String getParameterB(){ return parameteB; } private void readConfig(){ Properties p = new Properties(); InputStream in = null; try{ in =AppConfig.class.getResourceAsStream("AppConfig.properties"); p.load(in); this. ParameterA = p.getProperty("paramA"); this. ParameterB = p.getProperty("paramB"); }catch(IOException e){ e.printStackTrace(); }finally{ try{ in.close(); }catch(IOException e){ e.printStackTrace(); } } } }
测试客户端
AppConfig config= AppConfig.getInstance(); String paramA = config.getParameterA(); String paramB =config.getParameterB();下面是一种比较完美的代码构造,既可以使单例模式线程安全,也可以实现延迟加载
public class Singleton{ //内部类,该内部类的实例与外部类的实例没有绑定关系,而且只有被调用时才会被装载,从而实现了延迟加载 private static class SingletonHolder{ private static Singleton instance =new Singleton(); } private Singleton(){ //私有化构造方法 } public static Singleton getInstance(){ return SingletonHolder.instance; } }当getInstance第一次被调用的时候,它第一次读取SingletonHolder.instance,导致SingletonHoulder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,由于是静态的域,因此只会被虚拟机在装载类的时候初始化一次,并有虚拟机保证它的线程安全
建造者模式
在建造者模式里有个指导者角色,由指导者来管理建造者
建造者模式的目的是让设计和施工互相解耦
相同的方法,不同的执行顺序,产生不同的事件结果,可以考虑采用建造者模式
建造者模式是将复杂的内部创建封装在内部,对于外部调用的人,只需传入建造者和建造工具,至于内部是如何建造成成品的,调用者无需关心
如果将工厂模式看成一个汽车配件生产厂,生成不同类型的汽车配件,那么建造者模式就是一个汽车组装厂,通过对配件进行组装,返回一辆完整的汽车
工厂模式往往只关心你要的是什么,不关心这个东西的细节是什么,而建造者模式则关心的是这个东西的具体细节的创建
public class Car{ //汽车的实体类 private String head; //车头 private String body; //车身 private String tail; //车尾 //省略了setter、getter方法 } public abstract CarBuilder{ //汽车组装的抽象类 //组装车头 public abstract void makeHead(); //组装车身 public abstract void makeBody(); //组装车尾 public abstract void makeTail(); //返回组装好的汽车对象 public abstract Car getCar(); } public class JeepBuilder extends CarBuilder{ //吉普车组装类 Car car = new Car(); public void makeHead(){ car.setHead("Jeep Head"); }; public void makeBody(){ car.setHead("Jeep Body"); }; public void makeTail(){ car.setHead("Jeep Tail"); }; public Car getCar(){ return car; }; } //汽车组装操作的封装类 public class CarDirector { //此类完成设计,决定了执行哪些方法和执行顺序 public void makeCar(CarBuilder builder){ builder.makeHead(); builder.makeBody(); builder.makeTail(); } }
测试类
CarDirector director = new CarDirector(); CarBuilder builder = new JeeprBuilder(); director.makeCar(builder); //将吉普车的建造者传入指挥者中 Car car =builder.getCar(); //最后由建造者返回成品设想,如果现在要增加一个生产公共汽车的流程,应该怎样改动代码?
首先,新建一个继承了CarBuilder类的BusBuilder,测试类中,将JeeprBuilder改为BusBuilder即可,其他部分一点不用改动
原型模式
原型模式是指使用原型实例来指定创建对象的类型,并且通过拷贝这些原型创建新的对象
通过拷贝方法所创建的对象是全新的对象,它们在内存中有全新的地址
在Java语言中实现原型模式:
public class Prototype implements Cloneable { public Object clone() throws CloneNotSupportedException { //super.clone()调用的是Object的clone()方法 Prototype proto = (Prototype) super.clone(); return proto; } }
测试类
Prototype proto1= new Prototype(); Prototype proto2= proto1.clone();
在拷贝的时候有一个浅复制与深复制的概念
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。基本类型包括int、long、String等原始类型,同时满足以下两个条件的都不会被拷贝:1、类的成员变量,而不是方法内的变量;2、必须是一个对象,而不是一个基本类型
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
public class Thing implements Cloneable{ private ArrayList<String> arrayList= new ArrayList<String>(); public thing clone{ Thing thing = null; try{ thing =(Thing)supper.clone(); }catch(CloneNotSupportedExceptione){ //异常处理 } return thing; } public void setValue(String value){ this.arrayList.add(value); } public ArrayList<String> getValue(){ return arrayList; } }
测试类
Thing thing =new Thing(); thing.setValue("张三"); Thing cloneThing= thing.clone(); cloneThing.setValue("李四"); System.out.println(thing.getValue());预想的运行结果是"张三",实际上打印出来的是"张三,李四"
这时因为以上进行的是浅复制,thing中的arrayList并没有复制一个新的对象出来,thing和cloneThing实际上共用同一个arrayList,你改,我改,大家都能改,是一种非常不安全的方式
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() throwsIOException, ClassNotFoundException { /* 写入当前对象的二进制流 */ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); /* 读出二进制流产生的新对象 */ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = newObjectInputStream(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; }
如果需要实现深复制,可以通过序列化(Serialization)等方式来实现,序列化就是将对象写入到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中
能够序列化的对象其类必须实现Serializable接口
设计模式之创建型模式(5种)