设计模式的核心就是高类聚,低耦合,这对于代码的扩展性和后期的维护性是大有帮助的(扩展性和维护性指的是修改原来的代码,或者添加新的功能,要是能够在一个好的模式的代码上操作,那将是非常方便的。)
1,工厂模式:掩藏复杂的逻辑过程,只关心结果。常见的工厂模式分为,简单工厂,工厂方法,抽象工厂
抽象工厂虽然代码类有所增加,但是对于客户端而言,调用复杂度大大降低,这就是框架设计者要考虑的关键所在,减少用户调用API的复杂度,这个在spring的BeanFactory 中是怎么体现的呢?
spring 的工厂模式的BeanFactory 是用来生成各种bean的,这个有单例的bean,被代理的bean,最原始的bean,List类型的bean,各种不同作用域的bean,但是我们在使用的时候,获取bean就用一个getBean方法就可以获取想要的bean,不管你是什么样的bean,这样的效果产生就是用了工厂模式的抽象工厂
工厂方法感想:
public class SimpleCarFactory {
public static Car getCar(String name) {
if("benz".equalsIgnoreCase(name)) {
return new Benz();
}else if("bmw".equalsIgnoreCase(name)) {
return new Bmw();
}else if("audi".equals(name)) {
return new Audi();
}else {
return null;
}
}
}
对于调用端而言还算简单,想要什么车只需要传一个车的名称,但是看下SimpleCarFactory 这个工厂类把所有的车的生产都写在一个getCar的方法中,如果生产每辆车 的逻辑十分复杂的话 ,这个类就会有上万行,维护这个类就成了噩梦,(这就好比用一个servlet类写一个项目,这样实施也是可以的,但是写出来了,估计只有写出来的人看得懂,扩展性维护性完全没有)复杂的逻辑分散在每个if中,这每一个if都是一个生产车的复杂逻辑,造成定位都很难。这就有了演变后的 工厂方法,这个工厂方法是每个造车的都有自己的工厂,造成了类看上去会增多,但是这在实际开发中并没有什么影响,虚拟机也不会因为类变多了,运行效率降低,同时代码的可读性大大提高
public class TestCar {
public static void main(String[] args) {
Car car = SimpleCarFactory.getCar("benz");
System.out.println(car.getName());
}
}
2,单例模式:保证系统从启动到系统的停止,全过程只产生一个实例。
常见的单例有哪些?(在万千的世界中,即使是一片小小的树叶都是独一无二的)
为什了要有单例?
系统中的配置文件(如果内容一样,造成加载的资源的浪费,有多个也会造成选择性的冲突)
spring的容器ApplicationContext(有多个也没要,造成了资源的浪费)
生活中,公司的直接领导(假如有多个,你就不知到听谁的呢)
这就是我们选择单例的依据标准
单例的写法实际上有七种写法,现在提供一种最为经典的展示一下:(代码没有一句浪费,既能解决安全问题,又能解决性能问题)
//懒汉式(静态内部类的写法)
public class Singleton {
//1,private 保证别人不能修改,static 保证全局唯一
private static class LazyHolder{
private static final Singleton INSTANCE = new Singleton();
}
//2,将构造器私有化,防止外部调用,//这里说明一点,在反射面前,java的所有信息都是裸奔
//但是你想实例化我就抛出异常
private Singleton() {
throw new RuntimeException("禁止实例化。。");
}
//3,提供一个静态的方法给外部调用,final 保证别人不能覆盖
public static final Singleton getInstance() {
//方法中的逻辑是,是在用户调用的时候才开始执行的,方法中实现的逻辑需要分配内存,也是在调用的使用才分配的
return LazyHolder.INSTANCE;
}
}
3,委派模式(delegate)(dispatcher)
持有被委派的引用,和代理模式有点像。
类似公司中的项目经理和普通程序员,项目经理分配任务给程序员
,这种模式只关心结果,就好像是,干活是程序员的,功劳是项目经理的
这个和代理模式关心过程是有区别的,
为什么要使用这种模式?
主要是为了掩藏实现细节
IOC容器中,有一个Register的东西(为了告诉我们容器,在这个类初始化的过程中,需要做很多不同的逻辑处理,因此要有多个任务的执行者,分别实现各自的功能,有新的功能,只需要实现这个接口,就可以把功能加进去)
下面举一个例子
public interface IExccutor {
//定义一个任务的规范
public void doing();
}
public class ExecutorA implements IExccutor{
//假设这是一个程序员A完成任务的方法
public void doing() {
System.out.println("ExecutorA开始执行任务。。。");
}
}
public class ExecutorB implements IExccutor{
//假设这是一个程序员B完成任务的方法
public void doing() {
System.out.println("ExecutorB开始执行任务。。。");
}
}
//假设这是一个项目经理
public class Dispatcher {
private IExccutor exccutor;
//持有手下程序员的引用,可以命令去干活
public Dispatcher(IExccutor exccutor) {
this.exccutor = exccutor;
}
//这个就好比项目经理开始分配任务
//表面上干活的是他,其实是他手下的程序员
//结果就是,干活是你的,功劳是他的
public void doing() {
this.exccutor.doing();
}
}
public class Test {
public static void main(String[] args) {
IExccutor exccutor = new ExecutorA();
//这个项目经理实际派的是A程序员去干活,表面上看上去是自己在干活
Dispatcher dis = new Dispatcher(exccutor);
dis.doing();
}
}
4,策略模式(strategy)结果是一样的,但是过程是不一样的
什么是策略模式,完成一件事情有多重方式,比如你要去一个地方旅游,你可以选择坐飞机,也可以自己开车去,多种方案可供选择,这些方案就是一种种策略,java 中的比较器就是一个典型的例子
List<Integer> list = new ArrayList<Integer>();
Collections.sort(list, new Comparator<Integer>() {
//返回值0就是相等,返回值1就是前面大于后面
public int compare(Integer o1, Integer o2) {
//这里就是一些策略,你可以从大到小,或者从小到大排序
return 0;
}
});
}
5,原型模式prototype,java中的克隆clone方法就是原型模式的一个典型代表
就是一个现成的对象,这个对象里面已经设置好了值,当我们要创建第二个对象,并且要给这个对象赋值,而且赋值的内容要和先前的一模一样,这种场景下就诞生出 了原型模式
public class User implements Cloneable{
private int age;
private String name;
private List<String> friendList;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
//省略一堆setter/getter
}
测试原型拷贝
public class Test {
public static void main(String[] args) {
User user = new User();
user.setAge(22);
user.setName("zs");
List<String> fl = new ArrayList<String>();
fl.add("wangmazi");
user.setFriendList(fl);
try {
/*这个clone 是基于字节码拷贝出来的对象,没有走构造器,所以性能很高
当前走的是浅拷贝,也就是原始的对象的8个基本类型加上string的内容是完全复制过去了的,但是引用类型,他只是把地址拷贝过去了,造成了拷贝后的对象和原始对象公用的是一个,可能修改拷贝对象造成 了原始对象的数据也会被修改,这是有问题的,所以出现深拷贝解决方案,第二种方案就是利用反射,循环遍历原始的对象的属性一个个拷贝到新的对象中,这当面对原始对象中有大量的字段时,假如有上百个字段,还是可以节省不少事的,但是会有点点的性能问题,(毕竟反射是在运行时的操作),但这个也不是问题,很多ORM框架大量使用这种反射的方式操作很多字段的拷贝,其中Apache Commons Beanutils就有一个操作对象属性批量复制的方法BeanUtils,copyProperties(src,dest)*/
User copy = (User) user.clone();
System.out.println(user.getFriendList() == copy.getFriendList());//true
copy.getFriendList().add("wangmazi2");
System.out.println(user.getFriendList());//[wangmazi, wangmazi2]
System.out.println(copy.getName()+","+copy.getAge());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}