欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

18.设计模式(单例、简单工厂、工厂方法、静态工厂)与反射

程序员文章站 2022-03-09 20:30:14
...

设计模式与反射

1.设计模式

设计模式:前人在长期的软件开发过程中总结出来的一套用于解决特定问题的套路,我们使用这些套路可以写出优质的代码

共23中

1.1 简单工厂(静态工厂)模式

实现步骤:

1.首先定义一个产品类的共同接口Product(interface/abstract无要求)

2.分别有N个产品,均实现此共同接口ProductA、ProductB、ProductC 等

3.定义一个生产工厂类Factory ,根据输入类型生产对应的产品。方法为静态。

注:工厂类中的方法为静态的!!!,又称静态工厂模式

适用场景:简单工厂包含必要的判断逻辑,适用于if-else不多,(父子类结构中子类已经是固定的结构,比如省份是父类,而省份个数已经固定,不会轻易改变)

优点:可以实现创建对象的解耦,交给工厂去处理

缺点:不符合开闭原则(对扩展开放,对修改关闭),如果有新的子类,需要修改源代码

//汽车类,父类
public abstract class Car {
    public  abstract String getCarName();
}
//子类,奔驰类
public class Benz extends Car {
    @Override
    public String getCarName() {
        return "S600";
    }
}
//子类,奥地类
public class Audi extends Car {
    @Override
    public String getCarName() {
        return "Audi A7";
    }
}
/**
 *  汽车工厂类
 */
public class CarFactory {
    //静态的,因此为静态工厂
    public static Car createCar(String type) {
        Car car = null;
        if(type.equalsIgnoreCase("bmw")) {      
            car = new BMW();
        }else if(type.equalsIgnoreCase("benz")) {
            car = new Benz();
        }else if(type.equalsIgnoreCase("audi")) {
            car = new Audi();
        }else if(type.equalsIgnoreCase("dasauto")) {
            car = new DasAuto();
        }
        return car;
    }
   
​
    public static void main(String[] args) {
        System.out.println(CarFactory.createCar("benz").getCarName());
    }
}

1.2工厂方法模式

简单工厂模式缺点明显,可继续使用工厂方法模式来解决这个问题,创建不同的工厂,每添加一个新的子类,我们就添加一个新的工厂类

实现步骤:

1.将简单工厂模式的生产工厂类修改为接口IFactory

2.创建相应的N个具体产品工厂类实现此接口FactoryA、FactryB等

即:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

其余不变!!

适用场景:对象的创建逻辑比较复杂,需要组合其他类对象,做各种初始化操作。

(1)当一个类不知道它所必须创建的对象的类的时候

(2)当一个类希望由它的子类来指定它所创建的对象的时候

(3)当类将创建对象的职责委托给多个帮忙子类中的某一个,并且你希望将哪一个帮助子类是代理者--信息局部化的时候

优点:符合开闭原则,

缺点:每增加一个产品,都需要新增对应的生产工厂,导致增加额外的开发工作量

/**
 * 工厂接口类,提供汽车生产的方法
 * 具体是什么汽车  不指定
 */
public interface CarFactory {
    Car createCar();
}
//具体产品工厂类
public class BenzFactory implements CarFactory{
    @Override
    public Car createCar() {
        return new Benz();
    }
}

1.3抽象工厂方法模式

1.概念:

产品等级结构:即产品的继承结构,如一个抽象类是电视机,其子类是海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。

产品族:在抽象工厂模式中,产品族是指由同一工厂生产的位于不同产品等级结构中(同工厂的不同产品)的一组产品,如海尔电器工厂生产的海尔店实际、海尔冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中

引入场景:假设在工厂方法的基础上增加需求,现在要针对每种产品生产对应的赠品,难道要新增一个Gift的生产工厂吗?其实没有必要,因为在这个场景下,每种产品必须附带了赠品,所以我们可以利用原有的工厂来生产赠品。

步骤:1.定义一个共同的Gift接口Gift

2.增加对应的GiftA、GiftB、GiftC实现此接口

3.修改工厂类接口Factory,增加生产Gift的方法

4.修改工厂方法模式下的各个具体产品接口,FactoryA,FactoryB等

所以:有人称工厂方法是抽象工厂模式的特例的一种,就是只有一个要实现的产品接口。

优点:符合开闭原则

适用场景:适用于生产的产品属于某一个族群的情况,减少了工厂类。一个工厂也可以生产不止一种产品类

//共同的Gift接口
public interface Gift {
    String getGiftName();
}
//增加GiftA、GiftB、GiftC
public class GiftA implements Gift {
    @Override
    public String getGiftName() {
        return "GiftA";
    }
}
//修改Factory接口,增加生产Gift的方法
//修改工厂方法模式下的FactoryA、FactoryB、FactoryC,增加生产Gift的方法

1.4单例设计模式

单例模式就是保证系统中一个类只有一个实例,也就是说只能自己new自己并且实例唯一对外提供。

饿汉单例设计模式(推荐)

1.构造方法私有

2.private static修饰并创建唯一实例

3.提供一个公开的对外提供实例的静态方法

懒汉单例设计模式(有线程安全问题)

1.构造方法私有

2.private static修饰实例引用赋为为null

3.提供一个公开的对外提供实例的静态方法(内部判断是否存在实例,否则创建)

适用场景:

1.统计当前在线人数(网站计数器),用一个全局对象来记录

2.回收站。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

3.应用程序的日志(资源共享):一般日志内容是共享操作,需要在后面不断写入内容所以通常单例设计。

/**
 * 饿汉式单例设计模式(推荐)
 * @author asus
 * 两个静态
 */
public class HungrySingleton {
	private static HungrySingleton instance = new HungrySingleton();
	
	private HungrySingleton(){};
	
	public static HungrySingleton getHungrySingleton(){
		return instance;
	}
}
/**
 * 懒汉式单例设计模式
 * 线程不安全
 * @author asus
 */
public class LazySingleton {
	private static LazySingleton instance = null;
	
	private LazySingleton(){};	
	public static LazySingleton getLazySingleton(){
		//synchronized ("123锁") {
			if(instance == null){
			/*	try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}*/
				instance = new LazySingleton();
			}
		//}
		return instance;
	}
}

2.反射

反射:将类的各个组成部分封装为其他对象,这就是反射机制

好处:

1.可以在程序运行过程中,操作这些对象

2.可以解耦,提高程序的可扩展性

2.1获取Class对象的三种方式

1.Class.forName("全类名"):将字节码文件加载进内存,返回Class对象。(反射的方式,推荐)

  • 多用于配置文件,将类名定义在配置文件中,读取文件,加载类

2.类名.class:通过类名的属性class获取

  • 多用于参数的传递

3.对象.getClass():getClass()方法在Object类中定义着

  • 多用于对象的获取字节码的方式

结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个

//获取Class对象方式1:new对象
Date data = new Date();
System.out.println(data.getClass());

//获取Class对象方式2:类名+.class
System.out.println(Date.class);

//获取Class对象方式3:反射(推荐使用)
 Class<?> dateClass = Class.forName("java.util.Date");

2.2获取或为构造方法赋值(Constructor类)

1.getConstructors() 获取构造器集合,可遍历,可创建实例newInstance()

2.getConstructor(类<?>... parameterTypes)

  • 获取构造方法,若不传参数类型,获取无参

  • 参数类型为String.class、int.class等

  • 可创建实例newInstance(参数)

3.getParameterCount():获取参数个数

4.getParameters() : 获取参数集合

getType() getName()

5.getDeclaredConstructors():忽略访问修饰符,暴力获取构造方法

package com.qfedu.Test5;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
/**
 * 	通过反射获取类的有参构造方法  
 * 	并且传入值  获取值
 * @author asus
 *
 */
public class Test2 {
	public static void main(String[] args) {
		try {
			 Class<?> stuClass = Class.forName("com.qfedu.Test5.Student");
			 //可确定唯一的构造方法
			 Constructor<?> constructor = stuClass.getConstructor(String.class,String.class,int.class);
			 Object newInstance = constructor.newInstance("赵四","象牙山",20);
			 System.out.println(newInstance);
			 
			 Constructor<?>[] constructors = stuClass.getConstructors();
			 for(Constructor<?> con : constructors){
				 System.out.println(con.getParameterCount());
				 Parameter[] parameters = con.getParameters();
				 for(Parameter p : parameters){
					 System.out.print("构造方法中参数的名称" + p.getName() + "\t");
					 System.out.println("类型" + p.getType());
				 }
			 }
		}
        //异常处理catch块
	}
}

class Student{
	private String name;
	private String address;
	private int age;
	public String getName() {
		return name;
	}
	public Student() {
		System.out.println("Student无参构造方法");
	}
	public Student(String name, String address, int age) {
		this.name = name;
		this.address = address;
		this.age = age;
	}
	public Student(int age ,String name, String address) {
		this.name = name;
		this.address = address;
		this.age = age;
	}
}

2.3设置/获取方法(Method类)

1.getMethods()

2.getMethod("方法名", 参数类型.class); 获取单个方法

3.invoke(对象,参数值):执行方法

4.getDeclaredMethods():忽略访问修饰符,暴力获取

Class<?> gradeClass = Class.forName("com.qfedu.Test5.Grade");
			Object newInstance = gradeClass.newInstance();
			Method[] methods = gradeClass.getMethods();
			Method setNameMethod = null;
			Method setStuCountMethod = null;
			for(Method m : methods){
				//获取的是子类+父类的
				System.out.println(m.getName());
				if(m.getName().equals("setName")){
					setNameMethod = m;
				}
			}
			//获取单个方法名,需指定类型
			setStuCountMethod = gradeClass.getMethod("setStuCount", int.class);
			gradeClass.getDeclaredMethods();
			setNameMethod.invoke(newInstance, "三年二班");
			setStuCountMethod.invoke(newInstance, 30);
	System.out.println(gradeClass.getMethod("getName").invoke(newInstance));
			System.out.println(gradeClass.getMethod("getStuCount").invoke(newInstance));

2.4 设置/获取属性(Field类)

1.getDeclaredFields() :忽略访问修饰符,暴力获取

2.set(object obj, object value);设置属性值

3.setAccessible(true) : 暴力反射,属性私有时赋值需要先暴力反射

Class<?> schoolClass = Class.forName("com.qfedu.Test5.School");
			Object newInstance = schoolClass.newInstance();
			Field[] fields = schoolClass.getDeclaredFields();
			Field nameField = null;
			Field ageField = null;
			for(Field f : fields){
				System.out.println(f.getName() + "\t" + f.getType());
				if (f.getName().equals("name")) {
					nameField = f;
				}
			}
			ageField = schoolClass.getDeclaredField("age");
			nameField.setAccessible(true); //暴力反射
			nameField.set(newInstance, "赵四");
			ageField.setAccessible(true);
			ageField.setInt(newInstance, 22);
			System.out.println(schoolClass.getDeclaredField("name"));//属性私有,获取不到属性值
			System.out.println(schoolClass.getDeclaredField("age"));