经过 new 建立对象,会使得程序面向实现编程,先举个例子,某个果园里如今有两种水果,一种是苹果,一种是香蕉,有客户想采摘园子里的水果,要求用get()方法表示便可程序员
通常状况下,最直接的写法为:sql
public class Apple { public void get() { System.out.println("获得苹果"); } } public class Banana { public void get() { System.out.println("获得香蕉"); } } // 客户端 public static void one() { // 实例化一个apple Apple apple = new Apple(); // 实例化一个banana Banana banana = new Banana(); apple.get(); banana.get(); }
如上代码,一堆的水果类,必须等到运行时才能知道实例化哪个。一旦水果类有变化或者扩展,还要从新修改客户端类,一旦代码量多了,或者系统复杂了,修改的成本是很大的。数据库
那么能够用一种方法替代,就是工厂模式——把实例化的具体代码从应用中抽离,或者封装。工厂模式的变形比较多,本文只引伸简单工厂模式。编程
教科书的定义:bootstrap
简单工厂模式属于类的建立型模式,也叫静态工厂方法模式。它经过专门定义一个类来负责建立其余类的实例,目的是为了隐藏具体类的对象的建立过程,既不耽误对象的建立,也隐藏了建立过程,被建立的实例一般都具备共同父类设计模式
继续看水果的例子,后来果园有了新需求——用采摘到的水果作果汁,要求使用 doJuice(对应的水果)生产果汁。水果类的代码能够保持不变,客户端新加的其余代码以下:app
// 客户端 private static void doJuice(Apple apple) { apple.get(); System.out.println("作成果汁"); } private static void doJuice(Banana banana) { banana.get(); System.out.println("作成果汁"); } public static void one() { // 实例化一个apple Apple apple = new Apple(); // 实例化一个banana Banana banana = new Banana(); doJuice(apple); doJuice(banana); }
随着业务发展,后来果园又引进了大量新水果,好比橘子,西瓜,柿子,荔枝,葡萄,哈密瓜,火龙果等。若是继续用以前的代码,那么除了必须新加水果类以外,在客户端里还要分别为每一类水果添加对应的doJuice(水果)方法,然而水果那么多……使得代码的维护性,稳定性变差框架
为了增长程序的灵活性,能够作一些抽象,即把各个具体的水果都抽象化,能够选择抽象类或者接口去实现,如今要建立不带任何方法定义和成员变量的抽象化的类,首选的应该是接口ide
如图1所示,接口有扩展能力,也就是旧的接口能 extends 新接口,从而使得代码的扩展行为是可行的性能
使用接口的另外一个缘由和抽象类同样,都是为了不某个类被实例化,能够告诉编译器,以及一块儿协做开发的程序员,这个类不须要实例化,它只是为了对某些行为作出规范,谁想用,谁就去遵照这个规则便可。
public interface Fruit { void get(); } public class AppleA implements Fruit { @Override public void get() { System.out.println("苹果"); } } public class BananaA implements Fruit { @Override public void get() { System.out.println("香蕉"); } } // 客户端 private static void doJuiceB(Fruit fruit) { // Fruit 是接口,只须要一个方法 doJuiceB fruit.get(); System.out.println("作成果汁"); } private static void two() { // 使用接口的引用指向子类的对象,向上转型过程,用到了多态 Fruit apple = new AppleA(); Fruit banana = new BananaA(); Fruit orange = new OrangeA(); doJuiceB(apple); doJuiceB(banana); doJuiceB(orange); }
综上,接口的做用能够归纳为两个:
一、避免客户端去实例化某个类
二、向上转型的使用(多态)
继续看这个例子,客户只是想把果园采摘的水果作出果汁,客户做为调用者,只须要水果去作出果汁,而水果具体怎么获得的,其实客户不须要也不必关心,调用者不必为了喝果汁还花代价去亲自采摘水果……
以前的设计,客户端有一个传入接口类型参数的 doJuiceB(Fruit fruit); 方法。客户端调用该方法能够动态的作出不一样水果的果汁,如今把采集水果的代码单独放到一个类里,隐藏起来,分离变化的部分,咱们叫它工厂类,下面是代码实现。
// 工厂生产水果 // 对于客户端来讲,再也不直接简单粗暴的 new 一个水果的实例,而是把生成水果的实例的过程放到一个单独的类,把这个实例化的过程隐藏了起来……咱们叫它工厂类 public class FruitFactory { public static FruitC getApple() { return new AppleC(); } public static FruitC getBanana() { return new BananaC(); } } // 客户端 private static void doJuice(FruitC fruit) { fruit.get(); System.out.println("作成果汁"); } private static void three() { FruitC apple = FruitFactory.getApple(); FruitC banana = FruitFactory.getBanana(); doJuice(apple); doJuice(banana); }
简单工厂模式解决的问题是如何去实例化一个合适的对象,简单工厂模式的核心思想就是有一个专门的类来负责建立实例。具体来讲,把产品看为是一系列的类的集合,这些类由某个抽象类或者接口派生出一个对象树,工厂类产生一个合适的对象来知足客户的要求,从而把对象的建立过程进行封装
若是简单工厂模式所涉及到的具体产品之间没有共同的逻辑,那么就可使用接口来扮演抽象产品的角色,若是具体产品之间有逻辑上的联系,就把这些共同的东西提取出来,放在一个抽象类中,而后让具体产品继承抽象类,以实现代码复用,如图2所示。借用高斯林(Java之父)所说:
共同的东西老是应该抽象出来。在实际的的使用中,抽象产品和具体产品之间每每是多层次的产品结构
上面的设计,对于客户端来讲,再也不直接简单粗暴的 new 一个水果的实例,而是把生成水果的实例的过程放到一个单独的类,把这个实例化的过程隐藏了起来……咱们叫它工厂类,这个设计也叫简单工厂模式——它解决的问题是如何去实例化一个合适的对象。
简单工厂模式的核心思想就是:有一个专门的类来负责建立实例。具体来讲,把产品看着是一系列的类的集合,这些类是由某个抽象类或者接口派生出来的一个对象树,而工厂类用来产生一个合适的对象来知足客户的要求,从而把对象的建立过程进行封装,若是简单工厂模式所涉及到的具体产品之间没有共同的逻辑,那么咱们就可使用接口来扮演抽象产品的角色,若是具体产品之间有逻辑上的联系,咱们就把这些共同的东西提取出来,放在一个抽象类中,而后让具体产品继承抽象类,为实现更好复用的目的,共同的东西老是应该抽象出来的。在实际的的使用中,抽象产品和具体产品之间每每是多层次的产品结构,如图:
下面看看教科书的定义:简单工厂模式属于类的建立型模式,也叫静态工厂方法模式,经过专门定义一个类来负责建立其余类的实例,目的是为了隐藏具体类的对象的建立过程,既不耽误对象的建立,也隐藏了建立过程。被建立的实例一般都具备共同父类
本例子里,苹果和香蕉都有一个共同的父类——水果,此时咱们专门定义一个类,负责建立其余类的实例,这个类叫简单工厂类,它有三个角色:
一、工厂(Creator)角色:简单工厂模式的核心,它负责实现建立全部实例的内部逻辑。工厂类能够被外界直接调用,建立所需的产品对象。
二、抽象产品(Product)角色:简单工厂模式所建立的全部对象的父类,它负责描述全部实例所共有的公共接口,或者抽象类。
3.具体产品(Concrete Product)角色:简单工厂模式所建立的具体实例对象,这些对象去继承或者实现抽象角色
不过,细细体味下,在工厂类里针对每个水果都有一个对应的获取水果的操做,这是一种很粗糙的设计,还能够更好,就是把每一个get方法抽象为一个公用的get方法,代码以下:
public interface FruitD { void get(); } ////////////////////////////// public class AppleD implements FruitD { @Override public void get() { System.out.println("苹果"); } } /////////////////////////////// public class BananaD implements FruitD { @Override public void get() { System.out.println("香蕉"); } } ////////////////////////////// public class FruitFactoryFour { public static FruitD getFruit(String type) { if ("apple".equalsIgnoreCase(type)) { return new AppleD(); } else if ("banana".equalsIgnoreCase(type)) { return new BananaD(); } else { System.out.print("error!"); } return null; } }
这样稍微好了点儿,把每一个水果对应的get方法抽象为一个公用的get方法,工厂类里根据传入的参数,去判断应该生成哪一个水果的对象,并把这个对象返回(依然是向上转型的使用),客户端只需简单的进行调用便可。
很是方便,也隐藏了具体产品的实例化过程,完美的完成了客户和水果厂的需求。
能够认为简单工厂模式的核心是工厂类,这个类含有必要的逻辑判断(if-else),能够决定在何时建立哪个类的实例,而调用者则能够免除直接建立对象的责任。简单工厂模式经过这种作法实现了对责任的分割,当系统引入新的产品的时候无需修改调用者。
虽然简单工厂模式分离了产品的建立者和消费者,有利于软件系统结构的优化,可是因为一切产品建立的业务逻辑都集中在一个工厂类中,致使了没有很高的内聚性,同时也违背了开闭原则。另外,简单工厂模式的方法通常都是静态的,而静态工厂方法让子类继承是可能被隐藏的,所以,简单工厂模式没法造成基于基类的继承树结构。
到了这里,其实又要想,不要过分的优化,不要为了使用设计模式而使用设计模式,若是是业务比较简单的场景,这样的简单工厂模式仍是很是好用的。但不管如何,繁琐的if-else判断仍是不太好,一旦判断条件稍微多点儿,if-else写起来就很是繁琐。
观察一些开源框架实现相似场景的代码,发现它们使用了 Java 的反射机制省去了判断的步骤,比以前的繁琐的 if-else 判断要好一些,以下代码。
public interface FruitE { void get(); } public class BananaE implements FruitE { @Override public void get() { System.out.println("香蕉"); } } public class AppleE implements FruitE { @Override public void get() { System.out.println("苹果"); } } // 新的工厂类 public class FruitFactoryFive { public static FruitE getFruit(String type) throws ClassNotFoundException, IllegalAccessException, InstantiationException { Class fruit = Class.forName(type); return (FruitE) fruit.newInstance(); } } // 客户端 private static void five() throws IllegalAccessException, InstantiationException, ClassNotFoundException { FruitE apple = FruitFactoryFive.getFruit("simpleFactory.five.AppleE"); FruitE banana = FruitFactoryFive.getFruit("simpleFactory.five.BananaE"); apple.get(); banana.get(); }
如此一来,使得工厂的扩展性变强了。
从 JVM 的角度看,使用 new 的时候,这个要 new 的类能够没有被 JVM 加载,可是使用 newInstance,就必须保证这个类已经加载且这个类已经连接,而完成这两个步骤的正是 Class 的静态方法 forName(......),该方法调用了启动类加载器(bootstrap加载器)去加载类(不初始化)。
Class 类的对象方法 newInstance 与静态方法 forName 其实是把 new 关键字作的事情分解为了两步:
一、加载某个类
二、初始化
这样分步调用构造器的好处是显而易见的,由于它的粒度更细,因此程序能够在实例化类的时候得到更好的灵活性,催生一些降耦手段。
事实上,Class 类的 newinstance 方法常常被各类框架使用,它是解耦合的利器之一,好比设计模式中最最多见的工厂模式。
固然,一些知名的开源框架使用了更高级的asm等字节码框架,能使反射操做的性能很是高效,而且还能修改已经编译的字节码,使得程序的灵活性变得很强。
可是依然不完美—客户端缺乏调用的灵活性,客户端必须传入严格对应类名的字符串,甚至还要包含完整的包名,才能实例化对应的类,稍微差一点儿,都会失败。故仍是前面说的,简单的业务通常使用if-else的方式传入字符串便可,而稍微复杂的,能够变为反射的方式实现,而反射实现工厂类,对于客户端又显得调用上不方便。一些开源框架使用了配置文件或者注解解决了该问题,如今Java世界的主流是约定优于配置,注解是主流。
String className = readConfig(); // 从配置文件中得到类的句柄 Class c = Class.forName(className); factory = (FruitE)c.newInstance();
利用配置文件消灭了写死的产品类名称,不管产品怎么变化,代码不会再修改,甚至能够更换类的子类,只要他们继承该类(实现接口)就能够。
固然进一步就是自定义注解处理器,实现本身系统的注解
JDBC是SUN公司提供的一套数据库编程接口。它能提供简单、一致的方式访问各类关系型数据库。Java经过JDBC能够执行SQL语句,并能对获取的数据进行处理,将变化了的数据存回数据库。用JDBC进行数据库访问时,要使用数据库厂商提供的驱动程序接口与DBMS进行交互。客户端要使用使用数据时,只须要和工厂交互便可,这就是典型的简单工厂模式的应用。使得程序员的代码量获得极大的简化。
操做步骤按照顺序依次为:
一、注册并加载数据库驱动,通常使用Class.forName();
二、建立与数据库的连接Connection对象
三、建立SQL语句对象preparedStatement(sql);
四、提交SQL语句,根据实际状况使用executeQuery()或者executeUpdate();
五、显示相应的结果
六、关闭数据库
工厂类是整个模式的关键所在,它包含必要的判断逻辑,可以根据外界给定的信息,决定究竟应该建立哪一个具体类的对象。
用户在使用时能够直接根据工厂类去建立所需的实例,而无需了解这些对象是如何建立以及如何组织的,有利于整个软件体系结构的优化
因为工厂类集中了全部实例的建立逻辑,这就直接致使一旦这个工厂出了问题,全部的客户端都会受到牵连;
因为简单工厂模式的产品基于一个共同的抽象类或者接口,这样一来,产品的种类增长的时候,即有不一样的产品接口或者抽象类的时候,工厂类就须要判断什么时候建立何种种类的产品,这就和建立何种种类产品的产品相互混淆在了一块儿,违背了单一职责,致使系统丧失灵活性和可维护性。
简单工厂模式违背了“开放封闭原则”,由于当新增长一个产品的时候必须修改工厂类,相应的工厂类就须要从新编译一遍。
一句话:虽然简单工厂模式分离产品的建立者和消费者,有利于软件系统结构的优化,但因为一切逻辑都集中在一个工厂类中,致使了没有很高的内聚性,同时也违背了“开放封闭原则”。另外,简单工厂模式的方法通常都是静态的,而静态工厂方法是没法让子类继承的,所以,简单工厂模式没法造成基于基类的继承树结构
Java中有5类建立对象的方式
一、new
二、反射,Class.newInstance()或Contructor.newInstance(),其本质是同样的,都采用了反射机制
三、clone方法
四、反序列化
五、JNI