构建型模式
工厂模式
1. 首先要明确,传统创建对象的方式是new一个对象,硬编码不利于程序的松耦合。工厂模式则提供了一种将对象创建过程封装起来的思想,是一种用于封装对象的设计模式。
2. 主要分为三类:简单工厂模式,工厂方法模式,抽象工厂模式
简单工厂模式:最为实用,大量创建某对象可以使用,但是可维护性,可扩展性很差
实现过程:
创建披萨抽象类,封装基本的方法,若某个方法细节不同子类有区别则以抽象方式声明
public abstract class Pizza { String name; //准备的原材料,不同披萨不一样,所以作为抽象类 public abstract void prepare(); public void bake(){ System.out.println(name + "烤"); } public void cut(){ System.out.println(name + "切"); } public void box(){ System.out.println(name + "打包"); } public void setName(String name) { this.name = name; } }
创建其具体实现子类,满足不同需求
public class ChessPizza extends Pizza{ @Override public void prepare() { System.out.println("准备奶酪披萨原料"); } } public class BeefPizza extends Pizza{ @Override public void prepare() { System.out.println("准备牛肉披萨原料"); } }
创建工厂类,创建流程可以以静态方式实现,这样调用时无需实例化
public class SinpleFactory { public static Pizza createPizza(String type){ Pizza pizza = null; switch (type){ case "beef": pizza = new BeefPizza(); pizza.setName("BeefPizza"); break; case "chess": pizza = new ChessPizza(); pizza.setName("ChessPizza"); break; } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
最后,用户操作类,只需让用户指定需要得到的披萨种类,而无需知道制作细节
public class OrderPizza { public Pizza getOrderPizza(String type){ Pizza pizza = SinpleFactory.createPizza(type); return pizza; } }
工厂方法模式:就是将简单工厂模式的实例化过程延迟到子类进行,,这样,随着生产的产品越来越多时,工厂类不会变成超级类,当需要某个新产品时,只需要增加子类工厂即可,用户调用时同样只需要通过对应子类工厂调用即可,对应工厂已经将产品的创建过程进行了完美地封装。
抽象工厂模式:对工厂方法模式进行进一步的优化,可以提取出公共的工厂接口,这样在创建时指定了具体的工厂类后,在使用时就无需再关心是哪个工厂类,只需要将此工厂当作抽象的 接口使用即可,也正因如此,工厂的替换也将变得十分容易,整个底层逻辑和java的多态特性有着密切联系。
单例模式
1. 有效避免对象重复创建,节约空间提升效率,适用于全局只需要一个实例的场景
2. 能正确实现单例模式的实现方式总共有7种,但是只推荐使用双重检查和静态内部类两种方式进行实现。
饿汉式(静态常量)Ⅰ
构造器私有化
在类内部创建该类对象
向外提供方法获取该类对象
public class SingleTon { private SingleTon(){ } private static final SingleTon singleTon = new SingleTon(); public static SingleTon getInstance(){ return singleTon; } }
基于classloader机制避免了多线程同步问题
没用达到懒加载的效果,如果不使用会导致内存的浪费
饿汉式(静态代码块)Ⅱ
public class SingleTon {
private static SingleTon singleTon;
static {
singleTon = new SingleTon();
}
private SingleTon(){
}
public static SingleTon getInstance(){
return singleTon;
}
}
懒汉式(线程不安全)Ⅲ
构造器私有化
定义该对象属性但是不创建实例
向外部提供一个公有静态方法,在调用时再创建,且之创建一次返回该对象的实例,以后再获取直接返回第一次创建的实例
public class SingleTon { private static SingleTon singleTon; private SingleTon(){ } public static SingleTon getInstance(){ if(singleTon == null){ return new SingleTon(); } return singleTon; } }
实现了懒加载的效果,但是只限于单线程,有潜在风险
懒汉式(线程安全)Ⅳ
public class SingleTon {
private static SingleTon singleTon;
private SingleTon(){
}
//加入同步处理的代码
public static synchronized SingleTon getInstance(){
if(singleTon == null){
return new SingleTon();
}
return singleTon;
}
}
解决了线程不安全的问题
效率太低
双重检查(推荐)🔴🟠🟡🟢🔵🟣🟤⚫⚪Ⅴ
在属性上必须加入volatile修饰,这样属性值一旦发生变化会立即同步,即使多线程都进入了该方法内部,也会因为双重检查不会创建多个实例。
在返回实例的方法中进行双重检查
public class SingleTon { //加入volatile修饰,一但该值修改会立刻同步,可以理解为一种轻量级的同步 private static volatile SingleTon singleTon; private SingleTon(){ } public static SingleTon getInstance(){ if(singleTon == null){ synchronized (SingleTon.class){ if(singleTon == null){ singleTon = new SingleTon(); } } } return singleTon; } }
效率高,线程安全,懒加载
推荐使用
静态内部类(推荐)🔴🟠🟡🟢🔵🟣🟤⚫⚪Ⅵ
public class SingleTon {
private SingleTon(){ }
//在类加载时该静态内部类不会被加载,在使用返回实例方法调用其属性时才会进行装载,jvm虚拟机底层保证了其线程安全
private static class SingleTonInstance{
private static final SingleTon singleTon = new SingleTon();
}
public static SingleTon getInstance(){
return SingleTonInstance.singleTon;
}
}
采用类装载机制保证了初始化实例只有一个线程
调用方法才会装载静态内部类,从而完成实例化,类的静态属性只会在第一次加载时初始化,JVM保证了线程的安全(类初始化时别的线程无法进入)
线程安全,实现了延迟加载(懒加载),效率高
枚举Ⅶ
线程同步
能防止反射和反序列化创建新对象
public enum SingleTonEm { INSTANCE; //定义一个属性 }
建造者模式
1. 创建过程稳定,配置多变的对象。类似一种自选组件来构成一个实体的过程,这些组件只需选择而无需知道如何去创建。
2. 明确该模式的四个角色
Product 产品角色
Builder 抽象建造者
ConcreteBuilder 具体建造者
Director 指挥者
代码模拟
指定产品角色,也就是最终需要得到的实体
//probvider 产品 public class House { private String baise; private String wall; private String roofed; public String getBaise() { return baise; } public void setBaise(String baise) { this.baise = baise; } public String getWall() { return wall; } public void setWall(String wall) { this.wall = wall; } public String getRoofed() { return roofed; } public void setRoofed(String roofed) { this.roofed = roofed; } }
指定抽象建造者,指定基本流程,这个类中的方法由具体建造者进行实现,这个过程体现该模式的 创建过程稳定 的条件。
//Builder 抽象建造者 public abstract class HouseBuilder { protected House house = new House(); public abstract void buildBasic(); public abstract void buildWalls(); public abstract void rooted(); public House buildHouse(){ return house; } }
指定具体建造者,实现基本流程,体现 配置多变 的需求
//ConcreteBuilder 具体建造者 public class CommonHouse extends HouseBuilder { @Override public void buildBasic() { System.out.println("普通地基"); } @Override public void buildWalls() { System.out.println("普通墙"); } @Override public void rooted() { System.out.println("普通顶"); } }
//ConcreteBuilder 具体建造者 public class HighHouse extends HouseBuilder { @Override public void buildBasic() { System.out.println("高楼地基"); } @Override public void buildWalls() { System.out.println("高楼墙"); } @Override public void rooted() { System.out.println("高楼顶"); } }
编写指挥者,指挥者将指定生产产品的流程,当然需要通过接口聚合具体建造者类,可以通过构造器或者set方法
//Director 指挥者 public class HouseDirector { HouseBuilder houseBuilder = null; //两种方式传入houseBuilder public HouseDirector(HouseBuilder houseBuilder) { this.houseBuilder = houseBuilder; } public void setHouseBuilder(HouseBuilder houseBuilder) { this.houseBuilder = houseBuilder; } //如何建造房子流程,交给指挥者 public House constructHouse(){ houseBuilder.buildBasic(); houseBuilder.buildWalls(); houseBuilder.rooted(); return houseBuilder.buildHouse(); } }
实际调用
public class Test { public static void main(String[] args) { // 在指挥者传入具体建造者指定建造的具体产品 HouseDirector houseDirector = new HouseDirector(new HighHouse()); // 调用方法返回对象 House house = houseDirector.constructHouse(); } }
原型模式(prototype)
1. 用原型实例指定创建对象的种类,并通过拷贝这些实例创建新的对象,原型模式默认进行的是浅拷贝,也就是通过实现cloneable接口进行clone,但是这样如果克隆对象中存在其他对象的话,对该对象属性进行修改会导致所有克隆类中该属性随之变化,也就是说,复制的知识指向该对象的指针。
浅拷贝
实现 Cloneable接口,重写clone()方法,通过super调用父类方法实现克隆
如果成员变量有引用类型,那么拷贝是是引用传递,若对该引用进行修改,则也会对拷贝的对象同时进行联动修改
public class Sheep implements Cloneable{
private Integer id;
private String name;
public Sheep(Integer id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Sheep{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
@Override
protected Sheep clone() throws CloneNotSupportedException {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sheep;
}
}
深拷贝(推荐使用序列化的方式)
如果没用对象作为成员变量的话则没用必要使用深拷贝
方式一,进行深拷贝时要单独对对象进行处理,而这个对象相应也要进行拷贝处理,若其中还有对象,则需要一直处理,不推荐
方式二,通过序列化的方式进行拷贝,这个方法存在于需要拷贝的内部,通过this来将对象以对象流的方式输出。
//深拷贝,序列化方式,推荐 public Object deepClone(){ ByteArrayInputStream bis = null; ByteArrayOutputStream bos = null; ObjectInputStream ois = null; ObjectOutputStream oos = null; try { //序列化 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this);//将当前对象以对象流的方式输出 //反序列化 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); Sheep copysheep = (Sheep) ois.readObject(); return copysheep; } catch (Exception e) { e.printStackTrace(); }finally { try { bis.close(); bos.close(); oos.close(); ois.close(); } catch (IOException e) { e.printStackTrace(); } } return null; }
该模式意义
创建新的对象较为复杂时,使用该模式可以简化对象的创建过程,能提高效率
原型模式默认进行的是浅拷贝哦💙💚💛(默认使用的是clone方法,类需要实现Cloneable接口)
没有重新初始化对象,而是动态获取对象的状态,即克隆的就是其当前的状态
原生对象变化,此时进行克隆的对象也会相应发生变化
深克隆可能需要复杂代码,推荐使用序列化方式
该模式需要为每一个类配备一个克隆方法,对于新类简单,但是对于创建好的需要改造则违反了ocp原则
深克隆:深复制把要复制的对象所引用的对象都复制了一遍。
拷贝需要实现Cloneable, Serializable两个接口,重写clone方法⚠⚠⚠