这里总结一下学习过程中遇到过的设计模式,并补充一些其它常见的设计模式
创建型模式、结构型模式…
参考: 地址1、 地址2
单例模式
单例模式:保证一个类仅有一个实例,并提供一个全局的访问点。
优点和使用场景
使用场景:日志对象、应用配置、线程池等。
优点:
- 对于频繁使用的对象,可以节省创建对象所花费的时间。以及多个对象对系统内存的占用。
- 利用单例模式,可以实现在需要使用时才创建对象,而不像全局变量,一直占用资源。
单例模式的实现
两种实现方式:
- 饿汉方式 -> 单例的实例在类装载时创建
- 懒汉方式 -> 单例的实例在第一次被使用时创建
注:单例类必须要有一个 private 访问级别的构造方法,static 的成员变量 和 方法。
饿汉模式
1.常用方式
public class Single{
private static Single uniqueInstance = new Single();
private Single(){}
public static Single getInstance(){
return uniqueInstanc;
}
}
JVM 加载 Single 类时,就自动创建实例
在静态初始化器中创建实例,保证了线程安全
2.枚举方式
public enum Single {
INSTANCE;//定义一个枚举的元素,即Single实例
public Single getInstance(){
return INSTANCE;
}
public void print(){
System.out.print("序列化");
}
}
简洁、自动支持序列化机制,线程安全
懒汉模式
1.非线程安全版本
public class Singleton {
private static Singleton uniqueInstance;
private Single(){}
public static Single getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Single();
}
return uniqueInstance;
}
}
在第一次使用时(调用getInstance)才创建实例
当多个线程同时调用 getInstance 方法会出现问题
2.synchronized 版本,修改 getInstance 方法
public static synchronized Single getInstance() {
if (instance == null) {
uniqueInstance = new Single();
}
return uniqueInstance;
}
缺点: synchronized 锁会造成时间的消费,还可能发送阻塞
3.双重检查加锁版本
public class Single {
//volatile保证,初始化后实例被修改后的可见性
private volatile static Single uniqueInstance;
private Single() {
}
public static Single getInstance() {
if (uniqueInstance == null) {
//第一次使用才会执行同步代码块
synchronized(Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Single();
}
}
}
return uniqueInstance;
}
}
相比直接使用 synchronized 大大节省了时间
4.静态内部类版本
public class Singleton {
private static class SingleHolder {
private static final Single INSTANCE = new Single();
}
private Single(){}
public static final Single getInstance() {
return SingletonHolder.INSTANCE;
}
}
当调用 getInstance 才会装载 SingletonHolder 内部类
工厂模式
**工厂模式:**在基类中定义创建对象的一个接口,让子类决定实例化哪个类,工厂方法让一个类的实例化延迟到子类中进行。
工厂模式有三种分类:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
工厂模式的作用:
- 降低耦合度(将对象的创建和使用分开)
- 降低代码重复
- 降低维护成本(业务逻辑变化时,只需修改工厂)
简单工厂模式
简单工厂模式不符合 开发-封闭原则,所以不是23中常用的设计模式之一
角色分配
- 抽象产品角色:描述所有实例所共有内容的公共接口
- 具体产品角色:创建目标,实现接口的实现类
- 工厂角色:负责实现创建所有实例的内部逻辑
简单工厂模式实现
1.创建抽象产品角色,即接口
public interface User{
void name();
}
2.创建具体产品角色,即实现类
(1)学生
public class Stu implements User{
public void name(){
System.out.println("Students...");
}
}
(2)老师
public class Teach implements User{
public void name(){
System.out.println("Teachers...");
}
}
3.创建工厂角色,即工厂类
public class UserFactory{
public static User getUser(String name){
if(name==null)
return null;
if(name.qualsIgnoreCase("STUDENTS")){
return new Stu();
}else if(name.qualsIgnoreCase("TEACHERS")){
return new Teach();
}
}
return null;
}
4.使用
User stu = UserFactory.getUser("Students");
stu.name();
User tea = UserFactory.getUser("Teachers");
tea.name();
由上面步骤可知,当我们增加 User 时,就需要修改工厂类中的 getUser) 方法,所以不符合开放-封闭原则
反射改善简单工厂
修改工厂类:
public class UserFactory{
public static Object getUser(Class<? extends User> clazz){
Object obj = null;
try{
objc = class.forName(clazz.getName()).newInsatnce();
}catch (Exception e){
e.printStackTrace();
}
return obj;
}
修改后虽然符合开放-关闭原则,但每次都需要传入具体产品类的全路径,比较麻烦(改善:反射+配置文件)
工厂方法模式
将简单工厂模式深化,使用最多的模式,不再使用统一的工厂类来创建所有对象,而是针对不同的对象提供不同的工厂,即每个对象对应一个工厂。
角色分配
- 抽象产品角色
- 具体产品角色
- 抽象工厂角色
- 具体工厂角色
工厂方法模式实现
1.在简单工厂的基础上,增加一个工厂接口
public interface Factory{
public User getUser();
}
2.创建具体工厂类
(1)学生工厂
public class StuFactory implements Factory{
public User getUser(){
return new Stu();
}
}
(2)老师工厂
public class TeaFactory implements Factory{
public User getUser(){
return new Teach();
}
}
3.使用
Factort stufactory = new StuFactory();
User stu = stufactory.getUser();
stu.name();
抽象工厂模式
将工厂方法模式深化,工厂类不单可以创建一种产品,而是可以创建一组产品。
抽象工厂模式实现
不同专业学生对应不同老师
1.抽象产品
(1)学生
public interface Stu{
public void study();
}
(2)老师
public interface Tea{
public void teach();
}
2.具体产品
(1)数学专业学生
public class Math_Stu implements Stu{
public void study(){
System.out.println("studing Math");
}
}
(2)计算机专业学生
public class Computer_Stu implements Stu{
public void study(){
System.out.println("studing Comp uter");
}
}
(3)数学专业老师
public class Math_Tea implements Tea{
public void teach(){
System.out.println("teaching Math");
}
}
(4)计算机专业老师
public class Computer_Tea implements Tea{
public void teach(){
System.out.println("teaching Computer");
}
}
3.抽象工厂接口
public interface Factory(){
public Stu trainStu();
public Tea trainTea();
}
4.具体工厂类
(1)数学班
public class Math_Factory implements Factory{
public Stu trainStu(){
return new Math_Stu();
}
public Tea trainTea(){
return new Math_Tea();
}
}
(2)计算机班
public class Computer_Factory implements Factory{
public Stu trainStu(){
return new Computer_Stu();
}
public Tea trainTea(){
return new Computer_Tea();
}
}
5.使用
Factory factory = new Math_Factory();
Stu student = factory.trainStu();
student.study();
Tea teacher = factory,trainTea();
teacher.teach();
建造者模式
建造者模式,是一种对象构建模式;一步步创建一个复杂的对象,允许用户通过指定复杂对象的类型和内容就可以构建,而不需要知道其内部的具体细节。
优缺点
优点:
- 将产品对象与创建过程解耦
- 不同的绝体建造者对应不同的产品对象
- 可以精细的控制产品的创建过程
- 增加新的具体建造者无需修改源代码
缺点:
- 不适合产品之间差异过大的情况
- 不适合产品内部很复杂的情况
和抽象工厂模式的区别
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
角色分配
- 产品角色
- 抽象建造者
- 具体建造者
- 指挥者
建造者模式实现
1.产品类
public class Product{
private String partA;
private String partB;
private String partC;
...//get/set方法
}
2.抽象建造者
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;
}
}
3.具体建造者
public class ConcreteBuilder extends Builder{
public void buildPartA(){
...//get/set操作产品中的partA
}
public void buildPartB(){
...
}
public void buildPartC(){
...
}
}
4.指挥者
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();
}
}
5.使用
Bulider c = new ConcreteBuilder();//创建具体建造者
Director diretor = new Director(c);//创建相应产品的指挥者
Product product = diretor.construct();//用过指挥者获得产品
原型模式
原型模式,使用原型实例指定将要创建的对象类型,通过复制这个实例创建新的对象。
使用场景
- 对象种类繁多,无法整合成一个类
- 难用根据类生成实例
- 解耦框架与生成的实例
优缺点
优点:
- 简化对象的创建过程,通过已有实例可以提高实例创建效率
- 可以动态减少和增加产品类
- 可以使用深克隆保存对象的状态
缺点:
- 每个类都需配备克隆方法,对已有类进行修改时会很麻烦
- 深克隆的代码较为复杂
角色分配
- 抽象原型
- 具体原型
- 使用者
原型模式实现
1.抽象原型
//`java.lang.Cloneable 只是起到告诉程序可以调用clone方法的作用,它本身并没有定义任何方法
public interface Product extends Cloneable{
public abstract void use(String s);
//creatClone方法是用于复制实例的方法
public abstract Product creatClone();
}
2.具体原型
public class ProductImp implements Product{
ProductImp(String s){}
public void user(String s){}
//复制自己
public Product creatClone(){
Product p=null;
try{
p = (Product)clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return p;
}
}
3.使用者
public class Manager {
//保存实例的“名字”和“实例”之间的对应关系
private HashMap<String, Product> showcase=new HashMap<String, Product>();
//register方法将接收到的一组“名字”和“Product接口”注册到showcase中。这里Product是实现Product接口的实例,具体还未确定
public void register(String name ,Product product){
showcase.put(name, product);
}
public Product create(String productname){
Product p=showcase.get(productname);
return p.creatClone();
}
}
4.使用
Manager manager = new Manager();
ProductImp demo = new ProductImp("x");//具体原型实例
manager.register("show",demo);
Product p = manager.create("show");//依据具体原型实例创建新的实例
p.user("test");