Java设计模式之创建型模式
1.学习的作用
初学者的话,建议经常看一看设计模式,有利于代码的理解和书写。
我认为java设计模式最大的作用就是对代码的理解和对底层开发的认识,很多像spring、tomcat、springmvc等等都是用到了java的设计模式,而且如果理解透彻,对代码的封装、类的解耦有很大的帮助。
2.设计模式的六大原则
2.1开闭原则
在应用需求改变时,在不修改软件实体的源代码的前提下,能够扩展模块的功能,使其满足新的需求。
对测试人员来讲,只需要测试扩展的代码即可。提高代码的可复用性。
2.2里氏代换原则
官方大概的解释就是,LSP必须满足基类与子类的关系才能够存在。任何一个基类出现的地方,子类一定可以出现。基类是抽象,子类是具体。
2.3依赖倒转原则
上层模块不依赖下层模块,两者都应该依赖抽象;抽象不依赖具体,但具体依赖抽象。其核心思想就是面向接口编程。
实现开闭原则的重要途经之一,降低客户与实现模块之间的耦合。
2.4接口隔离原则
将庞大的接口拆分称小的具体的接口,每个类都建立其他们需要的专用接口。
提高类的内聚性、降低他们之间的耦合性,提高系统的灵活性和可维护性。
2.5迪米特法则
一个实体尽量减少和其他实体之间发生相互作用,可以通过第三方转发调用。其目的是降低类之间的耦合度,提高系统功能模块之间相对独立性。
2.6合成复用原则
在软件复用时。尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承实现。如果使用继承关系,必须严格遵循里氏代换原则。
3.创建型模式
主要是将对象的创建与使用分离。降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。
3.1.工厂方法模式
定义一个用于创建产品的接口,其每个子类可以生成一系列的相关产品。
分为(普通工厂模式、多个工厂方法模式、静态工厂方法模式)
3.1.1普通工厂模式
就是建立一个工厂类,对实现了同一个接口的一些类进行实例的创建。
public interface Animal {
public void say();
}
public class Dog implements Animal{
public void say() {
System.out.println("汪汪");
}
}
public class Cat implements Animal{
public void say() {
System.out.println("喵喵");
}
}
public class AnimalFactory {
public Animal produce(String animal) {
if("cat".equals(animal)) return new Cat();
else if ("dog".equals(animal)) return new Dog();
else return null;
}
}
public class FactoryTest {
public static void main(String []args) {
AnimalFactory animalFactory = new AnimalFactory();
Animal animal = animalFactory.produce("dog");
animal.say(); //输出"汪汪"
}
}
3.1.2多个工厂方法模式
对普通工厂方法模式的改进,防止因传递字符串出错而不能正确创建对象。
//修改创建工厂类
public class AnimalFactory {
public Animal produceCat() {
return new Cat();
}
public Animal produceDog() {
return new Dog();
}
}
public class FactoryTest {
public static void main(String []args) {
AnimalFactory animalFactory = new AnimalFactory();
Animal animal = animalFactory.produceCat();
animal.say();//输出"喵喵"
}
}
3.1.3静态工厂方法模式
将创建工厂类里面的方法设置为静态,不需要创建实例,直接通过类调用即可。
public class AnimalFactory {
public static Animal produceCat() {
return new Cat();
}
public static Animal produceDog() {
return new Dog();
}
}
public class FactoryTest {
public static void main(String []args) {
Animal animal = AnimalFactory.produceDog();
animal.say(); //输出"汪汪"
}
}
3.2抽象工厂模式
工厂方法模式,类的创建依赖工厂类,如果要拓展,必须对工厂类进行添加修改,这违背了闭包原则。所以抽象工厂模式解决了此问题,创建多个工厂类,这样一旦拓展,只需要添加工厂类即可,不需要修改之前的代码。
//Animal、Dog、Cat类和上面工厂方法模式的代码不变
public interface AnimalFactory {
public Animal produce();
}
public class DogFactory implements AnimalFactory{
public Animal produce() {
return new Dog();
}
}
public class CatFactory implements AnimalFactory{
public Animal produce() {
return new Cat();
}
}
public class FactoryTest {
public static void main(String []args) {
AnimalFactory factory = new DogFactory();
Animal animal = factory.produce();
animal.say(); //输出"汪汪"
}
}
3.3单例模式
在java应用中,单例对象能保证在一个jvm中只存在一个实例。这样减少类的创建可以减少系统的开销减少GC压力。
单例模式分类:饿汉式、单线程写法、多线程写法、多线程安全效率写法、静态内部类写法、枚举写法。
3.3.1饿汉式
类加载时就完成初始化,所以类加载较慢,但获取对象的速度快
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {}
public Singleton getInstance() {
return singleton;
}
}
3.3.2单线程写法
这种方式可以延迟类的加载速度,能够满足基本的需求。
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
3.3.3多线程写法
相比单线程写法,这种写法在多线程的情况下安全多了,但是也不能够保证没问题。因为instance = new Singleton();是分两步执行的,在jvm中不能够保证这两部操作的先后顺序,如果是先给Singleton分配空间(此时已经跳出synchronized),然后再赋值初始化,那就出错了。
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if(instance == null) {
synchronized (instance) {
if(instance == null)
instance = new Singleton();
}
}
return instance;
}
}
3.3.4静态内部类写法
相比多线程写法,此写法,再jvm内部机制中能够保证当一个类被加载时,这个类的加载过程时线程互斥的。这样当我们调用getInstance时,jvm能够帮我们保证instance只创建一次。
public class Singleton {
private Singleton() {
}
private static class SingletonFactory {
private static Singleton instance = new Singleton();;
}
public static Singleton getInstance() {
return SingletonFactory.instance;
}
}
3.3.5枚举写法
最佳的单例实现模式就是枚举模式,利用枚举的特性,让jvm来帮我们保证线程安全和单一实例的问题
public enum Singleton {
INSTANCE;
public void say() {
System.out.println("汪汪");
}
}
学到这里肯定有人问为什么不直接使用static修饰类去加载呢。因为单例可以被延迟初始话,静态类第一次加载就是初始化,如果系统庞大,在性能方面是个很大的考验。
3.4建造者模式
建造者模式就是将一个复杂对象的构造与它的表示分离,使同样的构造过程可以创建不同的表示。它是将一个复杂的对象分解为多个简单的对象,然后一步步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分时可以灵活选择的。
建造者模式由产品、抽象建造者、具体建造者、指挥者4个要素构成。
//产品
public class Product {
private String partA;
private String partB;
private String partC;
public void setPartA(String partA) {
this.partA = partA;
}
public void setPartB(String partB) {
this.partB = partB;
}
public void setPartC(String partC) {
this.partC = partC;
}
public void show() {
System.out.println("partA:"+partA+"partB:"+partB+"partC:"+partC);
}
}
//抽象建造者
public abstract class Builder {
protected Product product = new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
public Product getResult() {
return product;
}
}
//具体建造者(可以定义多个,这里只是测试用了一个)
public class BuilderA extends Builder{
public void buildPartA() {
product.setPartA("建造partA---1");
}
public void buildPartB() {
product.setPartB("建造partB---1");
}
public void buildPartC() {
product.setPartC("建造partC---1");
}
}
//指挥者
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
public class Client {
public static void main(String []args) {
Builder builder = new BuilderA();
Director director = new Director(builder);
Product product = director.construct();
product.show();//输出"partA:建造partA---1partB:建造partB---1partC:建造partC---1"
}
}
3.5原型模式
用一个已经创建的实例作为原型,通过复制来创建一个和原型相同或相似的新对象。原型模式的克隆分为浅克隆和深克隆。
//浅克隆
public class Cat implements Cloneable{
private Integer age;
public Cat clone() throws CloneNotSupportedException {
Cat cat = (Cat)super.clone();
return cat;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
public class Client {
public static void main(String []args) {
Cat cat1 =new Cat();
cat1.setAge(20);
try {
Cat cat2 = (Cat)cat1.clone();
System.out.println(cat1.getAge());//输出“20”
System.out.println(cat2.getAge());//输出“20”
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
浅克隆:将一个对象复制后,基本数据类型的变量都会重新创建,但是引用类型,指向还是原来对象的指向。
深克隆:将一个对象复制后,基本数据类型和引用类型都被重新创建。完全彻底的复制。
public class Cat implements Cloneable,Serializable{
private Integer age;
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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
public class Client {
public static void main(String []args) {
Cat cat1 =new Cat();
cat1.setAge(20);
Cat cat2;
try {
cat2 = (Cat)cat1.deepClone();
System.out.println(cat1.getAge());
System.out.println(cat2.getAge());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}