007-设计模式
in 002-设计模式 with 0 comment

007-设计模式

in 002-设计模式 with 0 comment

1 反射机制

1.1 什么是Java反射

就是正在运行,动态获取这个类的所有信息。

1.2 反射机制的作用

1.3 反射机制的应用场景

1.4 反射机制获取类方法

//第一种方式:  
Classc1 = Class.forName("Employee");  
//第二种方式:  
//java中每个类型都有class 属性.  
Classc2 = Employee.class;  
//第三种方式:  
//java语言中任何一个java对象都有getClass 方法  
Employeee = new Employee();  
Classc3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee)  	

1.5 反射创建对象的方式

Class<?> forName = Class.forName("com.itmayiedu.entity.User");
// 创建此Class 对象所表示的类的一个新实例 调用了User的无参数构造方法.
Object newInstance = forName.newInstance();

1.5.1 实例化有参构造函数

Class<?> forName = Class.forName("com.itmayiedu.entity.User");
Constructor<?> constructor = forName.getConstructor(String.class, String.class);
User newInstance = (User) constructor.newInstance("123", "123");

1.6 反射创建api

方法名称作用
getDeclaredMethods[]获取该类的所有方法
getReturnType()获取该类的返回值
getParameterTypes()获取传入参数
getDeclaredFields()获取该类的所有字段
setAccessible()允许访问私有成员

1.6.1 使用反射为类私有属性赋值

// 为user对象私有属性赋值
Class<?> classUser = Class.forName("com.itmayiedu.entity.User");
// 获取到当前的所有属性
Field[] fields = classUser.getDeclaredFields();
for (Field field : fields) {
	System.out.println(field.getName());
}
// 获取当前所有的方法
Method[] declaredMethods = classUser.getDeclaredMethods();
for (Method method : declaredMethods) {
	System.out.println(method.getName());
}
// 初始化对象
User user = (User) classUser.newInstance();
Field declaredField = classUser.getDeclaredField("id");
// 标记为true 允许反射赋值
declaredField.setAccessible(true);
declaredField.set(user, "20");
System.out.println("使用反射机制给id赋值为:"+user.getId());

1.6.2 JDBC反射加载驱动

public class DemoJDBC {
    public static void main(String[] args) throws Exception {
        // 加载驱动类
        Class.forName("com.mysql.jdbc.Driver");
    
        // 通过DriverManager获取数据库连接
        String url = "jdbc:mysql://192.168.1.150/test";
        String user = "teamtalk";
        String password = "123456";
        Connection connection = (Connection) DriverManager.getConnection(
                url, user, password);
        
        PreparedStatement statement = (PreparedStatement) connection.prepareStatement(
                "insert persion (name, age) value (?, ?)");
        statement.setString(1, "hdu");
        statement.setInt(2, 21);
        statement.executeUpdate();
        
        ResultSet resultSet = statement.executeQuery("select * from persion");
        // 操作ResultSet结果集
        while (resultSet.next()) {
            // 第一种获取字段方式
            System.out.println(resultSet.getString(1) + " " + 
                    resultSet.getString(2) + " " + resultSet.getString(3));
        }
        // 关闭数据库连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}

1.6.3 禁止使用反射机制初始化

将构造函数为私有化

2 设计模式分类

设计模式”这个术语最初并不是出现在软件设计中,而是被用于建筑领域的设计中

1977 年,美国著名建筑大师、加利福尼亚大学伯克利分校环境结构中心主任克里斯托夫·亚历山大(Christopher Alexander)在他的著作《建筑模式语言:城镇、建筑、构造(A Pattern Language: Towns Building Construction)中描述了一些常见的建筑设计问题,并提出了 253 种关于对城镇、邻里、住宅、花园和房间等进行设计的基本模式。

1979 年他的另一部经典著作《建筑的永恒之道》(The Timeless Way of Building)进一步强化了设计模式的思想,为后来的建筑设计指明了方向。

1987 年,肯特·贝克(Kent Beck)和沃德·坎宁安(Ward Cunningham)首先将克里斯托夫·亚历山大的模式思想应用在 Smalltalk 中的图形用户接口的生成中,但没有引起软件界的关注。

直到 1990 年,软件工程界才开始研讨设计模式的话题,后来召开了多次关于设计模式的研讨会。

1995 年,艾瑞克·伽马(ErichGamma)、理査德·海尔姆(Richard Helm)、拉尔夫·约翰森(Ralph Johnson)、约翰·威利斯迪斯(John Vlissides)等 4 位作者合作出版了《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software)一书,在本教程中收录了 23 个设计模式,这是设计模式领域里程碑的事件,导致了软件设计模式的突破。这 4 位作者在软件开发领域里也以他们的“四人组”(Gang of Four,GoF)匿名著称。

直到今天,狭义的设计模式还是本教程中所介绍的 23 种经典设计模式。

2.1 设计模式的六大原则

2.1.1 里氏代换原则(Liskov Substitution Principle)(继承)

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科
里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能

2.1.2 依赖倒转原则(Dependence Inversion Principle)

这个是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

2.1.3 接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

2.1.4 迪米特法则(最少知道原则)(Demeter Principle)

为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。(减少依赖,相互独立)

2.1.5 合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承。

2.2 单例模式

2.2.1 什么是单例

保证一个类只有一个实例,并且提供一个访问该全局访问点

2.2.2 单例应用场景

2.2.3 单例优缺点

优点

缺点

2.2.4 单例创建方式

饿汉式:类初始化时,会立即加载该对象,线程天生安全,调用效率高。
懒汉式: 类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象,具备懒加载功能。
静态内部方式:结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。
枚举单例: 使用枚举实现单例模式 优点:实现简单、调用效率高,枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞, 缺点没有延迟加载。
双重检测锁方式 (因为JVM本质重排序的原因,可能会初始化多次,不推荐使用)

//饿汉式
public class SingletonDemo01 {
	// 类初始化时,会立即加载该对象,线程天生安全,调用效率高
	private static SingletonDemo01 singletonDemo01 = new SingletonDemo01();
	private SingletonDemo01() {
		System.out.println("SingletonDemo01初始化");
	}
	public static SingletonDemo01 getInstance() {
		System.out.println("getInstance");
		return singletonDemo01;
	}
	public static void main(String[] args) {
		SingletonDemo01 s1 = SingletonDemo01.getInstance();
		SingletonDemo01 s2 = SingletonDemo01.getInstance();
		System.out.println(s1 == s2);
	}
}
//懒汉式
public class SingletonDemo02 {
	//类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象。
	private static SingletonDemo02 singletonDemo02;
	private SingletonDemo02() {
   
	}
	public synchronized static SingletonDemo02 getInstance() {
		if (singletonDemo02 == null) {
			singletonDemo02 = new SingletonDemo02();
		}
		return singletonDemo02;
	}
	public static void main(String[] args) {
		SingletonDemo02 s1 = SingletonDemo02.getInstance();
		SingletonDemo02 s2 = SingletonDemo02.getInstance();
		System.out.println(s1 == s2);
	}
}
// 静态内部类方式
public class SingletonDemo03 {
	private SingletonDemo03() {
           System.out.println("初始化..");
	}
	public static class SingletonClassInstance {
		private static final SingletonDemo03 singletonDemo03 = new SingletonDemo03();
	}
	// 方法没有同步
	public static SingletonDemo03 getInstance() {
		System.out.println("getInstance");
		return SingletonClassInstance.singletonDemo03;
	}
	public static void main(String[] args) {
		SingletonDemo03 s1 = SingletonDemo03.getInstance();
		SingletonDemo03 s2 = SingletonDemo03.getInstance();
		System.out.println(s1 == s2);
	}
}
enum UserEnum {
	HTTP_200(200, "请求成功"),HTTP_500(500,"请求失败");
	private Integer code;
	private String name;
	UserEnum(Integer code, String name) {
		this.code = code;
		this.name = name;
	}
	public Integer getCode() {
		return code;
	}
	public void setCode(Integer code) {
		this.code = code;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
public class TestEnum {
	public static void main(String[] args) {
		System.out.println(UserEnum.HTTP_500.getCode());
	}
}
/使用枚举实现单例模式 优点:实现简单、枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞 缺点没有延迟加载
public class User {
	public static User getInstance() {
		return SingletonDemo04.INSTANCE.getInstance();
	}
	private static enum SingletonDemo04 {
		INSTANCE;
		// 枚举元素为单例
		private User user;
		private SingletonDemo04() {
			System.out.println("SingletonDemo04");
			user = new User();
		}
		public User getInstance() {
			return user;
		}
	}
	public static void main(String[] args) {
		User u1 = User.getInstance();
		User u2 = User.getInstance();
		System.out.println(u1 == u2);
	}
}
public class SingletonDemo04 {
	private SingletonDemo04 singletonDemo04;
	private SingletonDemo04() {
	}
	public SingletonDemo04 getInstance() {
		if (singletonDemo04 == null) {
			synchronized (this) {
				if (singletonDemo04 == null) {
					singletonDemo04 = new SingletonDemo04();
				}
			}
		}
		return singletonDemo04;
	}
}

2.2.5 单例防止反射漏洞攻击

在构造函数中,只能允许初始化化一次即可。

private static boolean flag = false;
	private SingletonDemo04() {
		if (flag == false) {
			flag = !flag;
		} else {
			throw new RuntimeException("单例模式被侵犯!");
		}
	}
	public static void main(String[] args) {
	}

2.2.6 如何选择单例创建方式

2.3 工厂模式

2.3.1 什么是工厂模式

实现了创建者和调用者分离,工厂模式分为简单工厂、工厂方法、抽象工厂模式

2.3.2 工厂模式好处

2.3.3 工厂模式分类

2.3.3.1 简单工厂模式

简单工厂模式相当于是一个工厂中有各种产品,创建在一个类中,客户无需知道具体产品的名称,只需要知道产品类所对应的参数即可。但是工厂的职责过重,而且当类型过多时不利于系统的扩展维护。
simpleFactory.png

public interface Car {
	public void run();
}
public class AoDi implements Car {
	public void run() {
		System.out.println("我是奥迪汽车..");
	}
}
public class JiLi implements Car {
	public void run() {
		System.out.println("我是吉利汽车...");
	}
}
public class CarFactory {
	 public static Car createCar(String name) {
		if (StringUtils.isEmpty(name)) {
             return null;
		}
		if(name.equals("奥迪")){
			return new AoDi();
		}
		if(name.equals("吉利")){
			return new JiLi();
		}
		return null;
	}
}
public class Client01 {
	public static void main(String[] args) {
		Car aodi  =CarFactory.createCar("奥迪");
		Car jili  =CarFactory.createCar("吉利");
		aodi.run();
		jili.run();
	}
}

简单工厂的优点/缺点

2.3.3.2 工厂方法模式

工厂方法模式Factory Method,又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。

public interface Car {
	public void run();
}
public class AoDi implements Car {
	public void run() {
		System.out.println("我是奥迪汽车..");
	}
}
public class JiLi implements Car {
	public void run() {
		System.out.println("我是吉利汽车...");
	}
}
public class JiLiFactory implements CarFactory {
	public Car createCar() {
		return new JiLi();
	}
}
public class AoDiFactory implements CarFactory {
	public Car createCar() {
	
		return new AoDi();
	}
}
public class Client {
	public static void main(String[] args) {
		Car aodi = new AoDiFactory().createCar();
		Car jili = new JiLiFactory().createCar();
		aodi.run();
		jili.run();
	}
}

2.3.3.3 抽象工厂模式

抽象工厂简单地说是工厂的工厂,抽象工厂可以创建具体工厂,由具体工厂来产生具体产品。
abstractFactory.png

//发动机
public interface Engine {
	void run();
	void start();
}
class EngineA implements Engine {
	public void run() {
      System.out.println("转的快!");
	}
	public void start() {
		 System.out.println("启动快,自动档");
	}
}
class EngineB implements Engine {
	public void run() {
      System.out.println("转的慢!");
	}
	public void start() {
		 System.out.println("启动快,手动档");
	}
}
//座椅
public interface Chair {
	   void run();
}
 class ChairA implements Chair{
	public void run() {
		System.out.println("可以自动加热!");
	}
	
}
 class ChairB implements Chair{
	public void run() {
		System.out.println("不能加热!");
	}
	
}
public interface CarFactory {
	// 创建发动机
	Engine createEngine();
	// 创建座椅
	Chair createChair();
}
public class JiLiFactory implements CarFactory  {
	public Engine createEngine() {
	
		return new EngineA();
	}
	public Chair createChair() {
		
		return new ChairA();
	}
}
public class Client002 {
	 public static void main(String[] args) {
		CarFactory carFactory=new JiLiFactory();
		Engine engine=carFactory.createEngine();
		engine.run();
		engine.start();
	}
	
}

2.3.4 简单工厂、工厂方法、抽象工厂之小结、区别

2.4 代理模式

2.4.1 什么是代理模式

通过代理控制对象的访问,可以详细访问某个对象的方法,在这个方法调用处理,或调用后处理。既(AOP微实现) ,AOP核心技术面向切面编程

proxy.png

2.4.2 代理模式应用场景

SpringAOP、事物原理、日志打印、权限控制、远程调用、安全代理 可以隐蔽真实角色

2.4.3 代理的分类

Jdk自带动态代理
Cglib 、javaassist(字节码操作库)

2.4.4 静态代理

由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

public interface IUserDao {
	void save();
}
public class UserDao implements IUserDao {
	public void save() {
		System.out.println("已经保存数据...");
	}
}
代理类
public class UserDaoProxy implements IUserDao {
	private IUserDao target;
	public UserDaoProxy(IUserDao iuserDao) {
		this.target = iuserDao;
	}
	public void save() {
		System.out.println("开启事物...");
		target.save();
		System.out.println("关闭事物...");
	}
}

2.4.5 动态代理

  • 代理对象,不需要实现接口
  • 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
  • 动态代理也叫做:JDK代理,接口代理

2.4.5.1 JDK动态代理

原理:是根据类加载器和接口创建代理类(此代理类是接口的实现类,所以必须使用接口 面向接口生成代理,位于java.lang.reflect包下)

实现方式

缺点
jdk动态代理,必须是面向接口,目标业务类必须实现接口

动态代理示例

// 每次生成动态代理类对象时,实现了InvocationHandler接口的调用处理器对象 
public class InvocationHandlerImpl implements InvocationHandler {
	private Object target;// 这其实业务实现类对象,用来调用具体的业务方法
	// 通过构造函数传入目标对象
	public InvocationHandlerImpl(Object target) {
		this.target = target;
	}
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result = null;
		System.out.println("调用开始处理");
		result = method.invoke(target, args);
		System.out.println("调用结束处理");
		return result;
	}
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
			IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		// 被代理对象
		IUserDao userDao = new UserDao();
		InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);
		ClassLoader loader = userDao.getClass().getClassLoader();
		Class<?>[] interfaces = userDao.getClass().getInterfaces();
		// 主要装载器、一组接口及调用处理动态代理实例
		IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
		newProxyInstance.save();
	}
}

2.4.5.2 CGLIB动态代理

利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
什么是CGLIB动态代理
使用cglib[Code Generation Library]实现动态代理,并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码

CGLIB动态代理相关代码

public class CglibProxy implements MethodInterceptor {
	private Object targetObject;
	// 这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
	public Object getInstance(Object target) {
		// 设置需要创建子类的类
		this.targetObject = target;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(target.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
	}
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("开启事物");
		Object result = proxy.invoke(targetObject, args);
		System.out.println("关闭事物");
		// 返回代理对象
		return result;
	}
	public static void main(String[] args) {
		CglibProxy cglibProxy = new CglibProxy();
		UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao());
		userDao.save();
	}
}

2.4.5.3 CGLIB动态代理与JDK动态区别

2.4.5.4 Spring中动态代理:

JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。
因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。

2.5 建造者模式

是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

builder.png

2.5.1 与工厂类模式的区别

工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。
与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

2.5.2 建造者模式角色:

2.5.3 建造者应用场景

2.5.4 使用场景:

2.5.5 实际案例

这里以游戏开发中人物的构造过程为例。在游戏中创建一个形象时,需要对每个部位进行创建。简化而言,需要创建头部,身体和四肢。

建立一个人物对象Person

public class Person {
	private String head;
	private String body;
	private String foot;
	public String getHead() {
		return head;
	}
	public void setHead(String head) {
		this.head = head;
	}
	public String getBody() {
		return body;
	}
	public void setBody(String body) {
		this.body = body;
	}
	public String getFoot() {
		return foot;
	}
	public void setFoot(String foot) {
		this.foot = foot;
	}
}

Builder抽象接口

public interface PersonBuilder {
	void builderHead();
	void builderBody();
	void builderFoot();
	Person BuilderPersion(); //组装
}

ConcreteBuilder实现Builder接口

public class ManBuilder implements PersonBuilder {
	private Person person;
	public ManBuilder() {
		person = new Person();//创建一个Person实例,用于调用set方法
	}
	public void builderHead() {
		person.setHead("建造者头部分");
	}
	public void builderBody() {
		person.setBody("建造者身体部分");
	}
	public void builderFoot() {
		person.setFoot("建造者头四肢部分");
	}
	public Person BuilderPersion() {
		return person;
	}
}

Director(调用具体建造者来创建复杂对象的各个部分)

public class PersonDirector {
	public Person constructPerson(PersonBuilder pb) {
		pb.builderHead();
		pb.builderBody();
		pb.builderFoot();
		return pb.BuilderPersion();
	}
	public static void main(String[] args) {
		PersonDirector pb = new PersonDirector();
		Person person = pb.constructPerson(new ManBuilder());
		System.out.println(person.getHead());
		System.out.println(person.getBody());
		System.out.println(person.getFoot());
	}
}

2.6 模板方法

定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
简化理解:定义一个模板,重复的代码全部在父类实现,不同的业务使用抽象方法由子类实现。

  1. 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
  2. 它在父类中提取了公共的部分代码,便于代码复用。
  3. 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
  1. 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象
  2. 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。

2.7 原型模式(克隆技术)

用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,Windows 操作系统的安装通常较耗时,如果复制就快了很多。在生活中复制的例子非常多,这里不一一列举了。

2.8 适配模式

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作

2.9 装饰模式

指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

  1. 采用装饰模式扩展对象的功能比采用继承方式更加灵活。
  2. 可以设计出多个不同的具体装饰类,创造出多个不同行为的组合。
  1. 装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。

2.10 外观模式

是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。

外观(Facade)模式是迪米特法则的典型应用

  1. 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
  2. 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
  3. 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
  1. 不能很好地限制客户使用子系统类。
  2. 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
    浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
    深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
    总之深浅克隆都会在堆中新分配一块区域,区别在于对象属性引用的对象是否需要进行克隆(递归性的)。

2.11 策略模式

该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理

  1. 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。
  2. 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
  3. 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
  4. 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
  5. 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
  1. 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
  2. 策略模式造成很多的策略类。

2.12 观察者模式

多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

  1. 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
  2. 目标与观察者之间建立了一套触发机制。

抽象目标AbstractSubject

public abstract class AbstractSubject {

    protected List<Observer> observers = new ArrayList<>();

    /**
     * 增加观察者方法
     *
     * @param observer
     * @return void
     * @throws
     * @author zhouxinlei
     * @date 2019-11-23 21:18:13
     */
    public void add(Observer observer) {
        observers.add(observer);
    }

    /**
     * 删除观察者方法
     *
     * @param observer
     * @return void
     * @throws
     * @author zhouxinlei
     * @date 2019-11-23 21:18:23
     */
    public void remove(Observer observer) {
        observers.remove(observer);
    }

    /**
     * 通知观察者方法
     *
     * @param
     * @return void
     * @throws
     * @author zhouxinlei
     * @date 2019-11-23 21:18:39
     */
    public abstract void notifyObserver();
}

具体目标ConcreteSubject

public class ConcreteSubject extends AbstractSubject {
    @Override
    public void notifyObserver() {
        System.out.println("具体目标发生改变...");
        System.out.println("--------------");
        observers.stream().forEach(item -> (item).response());
    }
}

抽象观察者Observer
public interface Observer {
void response();
}

**具体观察者1 ConcreteObserver1**
```Java
public class ConcreteObserver1 implements Observer {
    @Override
    public void response() {
        System.out.println("具体观察者1作出反应!");
    }
}

具体观察者2 ConcreteObserver2

public class ConcreteObserver2 implements Observer {
    @Override
    public void response() {
        System.out.println("具体观察者2作出反应!");
    }
}

test类

public class ObserverMain {
    public static void main(String[] args) {
        AbstractSubject subject = new ConcreteSubject();
        Observer obs1 = new ConcreteObserver1();
        Observer obs2 = new ConcreteObserver2();
        subject.add(obs1);
        subject.add(obs2);
        subject.notifyObserver();
        subject.remove(obs2);
        subject.notifyObserver();
    }
}

结果
observer.png