设计模式(2)浅谈创建型设计模式

设计模式概述

  • 设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。
  • 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件
  • 开发人员经过相当长的一段时间的试验和错误总结出来的。
    设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
  • 设计模式不是一种方法和技术,而是一种思想。
  • 设计模式和具体的语言无关,学习设计模式就是要建立面向对象的思想,尽可能的面向接口编程,低耦合,高内聚,使设计的程序可复用。
  • 学习设计模式能够促进对面向对象思想的理解,反之亦然。它们相辅相成。

设计模式的类型

总体来说,设计模式按照功能分为三类23种:

  • 创建型(5种) : 工厂模式、抽象工厂模式、单例模式(重点)、原型模式、构建者模式
  • 结构型(7种): 适配器模式、装饰模式、代理模式(重点) 、外观模式、桥接模式、组合模式、享元模式
  • 行为型(11种): 模板方法模式、策略模式 、观察者模式、中介者模式、状态模式、 责任链模式 、命令模式、迭代器模式、访问者模式、解释器模式、备忘录模式

创建型设计模式

	* 为什么要使用创建型设计模式?

		** Student stu = new Student(//需要10个构造参数); 
			*** 有些时候,构建细节太多,其实程序员一点也不想去关心,所以可以直接将创建对象的事情交给专业的人士
			*** 当一些类是通过第三方的jar包引用的,比如说BasicDataSource,我们基本上不了解如何去构造该对象。
	* 设计模式的使用注意事项:
		** 每个设计模式,都有自己的优缺点,我们要根据具体的实际情况,去进行选择合适的设计模式。

1、工厂模式

简单工厂

【只有一个工厂类】

  • 可以生产任何需要的对象,也称之为上帝/万能工厂。
    spring就是使用简单工厂实现了IoC容器
  • 以下的代码虽然是采取了简单工厂的写法,但是它存在的问题也很大
    • 缺点一:违反了开闭原则
      缺点二:该类太累,代码太重
    • 问题是如何改造呢?
      解决方案一:spring有一套改造方案。
      (spring中是通过配置文件和反射解决了简单工厂中的缺点)
      解决方案二:其他工厂方法模式
public class SimpleFactory{
			
	public Object getBean(String name){
				
		if("cat".equals(name)){
			return new Cat();
		} else if("cow".equals(name)){
				return new Cow();
		}。。。。。。
	}
}

工厂方法

  • 【工厂会是多个,生产的产品是一个】,针对不同需求,可以创造不同的工厂,那问题是如何创建工厂呢?

  • 和简单工厂不同的是:先有造工厂的方法(标准也叫接口),再根据标准去盖工厂,由这个工厂去创建不同的产品(产品在工厂方法中是抽象的)。

  • 举例
    SqlSessionFactory和DefaultSqlSessionFactory

如图:在这里插入图片描述
示例代码:

SqlSessionFactory 接口(工厂标准)

public interface SqlSessionFactory {

	SqlSession openSqlSession();
}

DefaultSqlSessionFactory 工厂去按照标准创建产品

public class DefaultSqlSessionFactory implements SqlSessionFactory {

	// 我既然不负责Configuration的解析,那么我就需要被外部注入一个configuration
	private Configuration configuration;

	public DefaultSqlSessionFactory(Configuration configuration) {
		this.configuration = configuration;
	}

	@Override
	public SqlSession openSqlSession() {
		return new DefaultSqlSession(configuration);
	}
}

DefaultSqlSession 类 DefaultSqlSessionFactory是生产的产品,但是他也是实现了Sqlsession(标准)的产品

public class DefaultSqlSession implements SqlSession {

	private Configuration configuration;

	public DefaultSqlSession(Configuration configuration) {
		this.configuration = configuration;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T selectOne(String statementId, Object param) {
		List<Object> list = this.selectList(statementId, param);
		if (list != null && list.size() == 1) {
			return (T) list.get(0);
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> List<T> selectList(String statementId, Object param) {
		MappedStatement mappedStatement = configuration.getMappedStatementById(statementId);
		if (mappedStatement == null) {
			return null;
		}
		try {
			// TODO Executor executor = new CachingExecutor();
			Executor executor = configuration.newExecutor();
			return (List<T>) executor.query(configuration, mappedStatement, param, null);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	@Override
	public int insert(String statementId, Object param) {
		// TODO Auto-generated method stub
		return 0;
	}
}

SqlSession 接口

public interface SqlSession {

	<T> T selectOne(String statementId, Object param);

	<T> List<T> selectList(String statementId, Object param);

	int insert(String statementId, Object param);
}

优缺点

  • 优点:
    工厂方法模式就很好的减轻了工厂类的负担,把某一类/某一种东西交由一个工厂生产;(对应简单工厂的缺点1)同时增加某一类”东西“并不需要修改工厂类,只需要添加生产这类”东西“的工厂即可,使得工厂类符合开放-封闭原则。
  • 缺点:
    对于某些可以形成产品族(一组产品)的情况处理比较复杂。

2、抽象工厂

【工厂会是多个,生产的产品是多个,而这多个产品是属于一个产品族】,其他的都和工厂方法是一致的。

  • 举例:
    比如生产一辆汽车或者一部手机,汽车零件或者手机零件,都是产品一个工厂生产的是一种品牌的一套零件。
    在这里插入图片描述

工厂模式区别

  • 简单工厂 : 使用一个工厂对象用来生产同一等级结构中的任意产品。(不支持拓展增加产品)
  • 工厂方法 : 使用多个工厂对象用来生产同一等级结构中对应的固定产品。(支持拓展增加产品)
  • 抽象工厂 : 使用多个工厂对象用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增
    加产品族)

3、构建者模式

和工厂模式的对比

  • 相同点:构建者模式和工厂模式都是创建对象的
  • 不同点:工厂更多的是批量创建相同属性的对象。而构建者是根据调用者的需求去私人定制对象

角色

  • 产品类:要生产的对象
  • 导演类:导演如何去定制一个对象
  • 构建者类:就是完成私人定制功能的具体类

举例a:创建Student

  • new Student();
  • 工厂
  • 创建出来的学生,可能是18了,也可能是20,可能是男,也可能是女

代码示例

StudentBuilder 构建者

public class StudentBuilder {

	// 需要构建的对象
	private Student student = new Student();

	public StudentBuilder id(int id) {
		student.setId(id);
		return this;
	}

	public StudentBuilder name(String name) {
		student.setName(name);
		return this;
	}

	public StudentBuilder age(int age) {
		student.setAge(age);
		return this;
	}

	public StudentBuilder father(String fatherName) {
		Father father = new Father();
		father.setName(fatherName);
		student.setFather(father);
		return this;
	}

	// 构建对象
	public Student build() {
		return student;
	}
}

BuildDemo 导演类

public class BuildDemo {

	public static void main(String[] args) {
		
		StudentBuilder builder = new StudentBuilder();
		// 决定如何创建一个Student
		builder.age(18).name("dili").father("wangjianlin");
		
		//根据不同的表示去创建对象(私人定制)
		Student student = builder.build();
		
		//builder.build(xxxxxxxx).build();
		System.out.println(student);
	}
}

Student 产品类

public class Student {

	private int id;
	private String name;
	private int age;
	
	// 子产品
	private Father father;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Father getFather() {
		return father;
	}

	public void setFather(Father father) {
		this.father = father;
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + ", father=" + father + "]";
	}	
}

举例b:mybatis中的MappedStatement的内部类Builder

  • 导演类:MapperBuilderAssistant
  • Builder类:MappedStatement#Builder(内部类)
  • 产品类:MappedStatement

反思:

  • 方式1:构建者模式 builder.username(“zhangsan”);

  • 方式2:User user = new User(); user.setUsername(“zhangsan”);

  • 区别在于

    • 方式1,你在私人定制的时候,还没有得到最终的对象。还不能使用对象。
    • 方式2,你在set的时候,该对象其实已经可以被其他人使用了

4、原型模式

不通过new关键字,直接复制对象(不走构造方法,走的是底层的C函数调用JVM内存对象的复制功能)

原型对象,复制算法,我就可以搞出来一个和他一样的对象

  • 如果有个对象,构造的时候,需要100个构造参数,此时new起来就非常费劲
  • A线程和B线程都对Student对象进行操作,A线程和B线程都想单独操作Student的数据,此时需要两个Student。
  • 举例:
    两女争一男,将男同志一分为二是最好的选择,皆大欢喜。

两种实现方式

浅拷贝

  • 只有对象的8种基本类型和String类型进行复制,而引用类型只是复制了引用,而没有将引用类型对应的对象进行复制。
  • 浅拷贝需要原对象实现Clonable接口(标记接口)
	/* 浅复制 */
public Object shallowClone() throws CloneNotSupportedException {
	// super.clone()其实就是调用了Object对象的clone方法
	// Object对象的clone方法是调用了native方法去在JVM中实现对象复制。
	// 此时不会调用构造方法去创建对象
	Prototype proto = (Prototype) super.clone();
	return proto;
}

深拷贝

  • 引用类型对应的对象,也在内存中复制了一个
  • 深拷贝需要原对象实现Serializable接口(标记接口)
	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();
	}

图示:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值