设计模式
设计模式(Design pattern),是一套被反复使用、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、保证代码可靠性、程序的重用性。
1995 年,GoF(Gang of Four,四人组)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式。
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。-->创建对象
结构型模式,共七种:[适配器模式]、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。-->对功能进行增强
行为型模式,共十一种:策略模式、模板方法模式、[观察者模式]、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、[中介者模式]、解释器模式。
一.单例模式
1.饿汉式:
类一加载到内存,先加载静态的,使用下面的代码赶紧new一个对象来使用
单例模式:
目的:保证一个类只产生一个对象
饿汉式:迫不及待的使用对象
public class Singleton {
//在自己内部定义自己一个实例
//注意这是private 只供内部调用
private static Singleton instance = new Singleton();
//如上面所述,将构造函数设置为私有
private Singleton(){
}
//静态方法,提供了一个供外部访问得到对象的静态方法
public static Singleton getInstance() {
return instance;
}
}
2.懒汉式:
1.先判断instance是不是空,如果是空,直接new
2.等下一次调用getInstance方法的时候再次判断是不是null,不是的话把第一次new出来的对象返回
3.这样线程不安全:
如果有一条线程进了if但是还没等new对象呢,另外一个线程也可以进if
再执行的话,由于两个线程都进了if,所以就会出现new两次的情况
4.怎么解决线程不安全问题:
在getInstance()方法上加一个synchronized关键字,变成同步的
public class Singleton {
/*
先不着急new对象,使用的时候调用getInstance方法的时候再new对象
*/
//和上面有什么不同?
private static Singleton instance = null;
//设置为私有的构造函数
private Singleton(){
}
//静态工厂方法
public static Singleton getInstance() {
//这个方法比上面有所改进
if (instance==null){
instance=new Singleton();
}
return instance;
}
}
3.小结
单例模式可以保证系统中一个类只有一个对象实例。
实现单例模式的步骤:
1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
2. 在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
3. 定义一个静态方法返回这个唯一对象。
二.多例模式
1.多例设计模式概述
多例模式,是一种常用的软件设计模式。通过多例模式可以保证系统中,应用该模式的类有固定数量的实例。多例类要自我创建并管理自己的实例,还要向外界提供获取本类实例的方法。
例如:
扑克牌程序,一个“扑克类”会创建固定的54个对象,不能多、也不能少。
麻将程序,一个“骰子类”会创建固定的2个对象,不能多、也不能少。
程序中需要用到“颜色的表示”,只能有三种颜色“红、绿、蓝”,一个“颜色类(Color)”应该只创建三个对象,来代 表这三个颜色。
多例模式的作用:使某个类,在程序运行期间,只能产生固定的几个对象,不能多、也不能少。
2.实现步骤
1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
2.在类中定义该类被创建对象的总数量,就3个,不能多,也不能少
3.在类中定义存放类实例的list集合
4.在类中提供静态代码块,在静态代码块中创建类的对象,存储到集合中
5.提供获取类实例的静态方法
3.代码实现
public class Multition {
// 定义该类被创建的总数量
private static final int maxCount = 3;
// 定义存放类实例的list集合
private static List list = new ArrayList();
// 构造方法私有化,不允许外界创建本类对象
private Multition() {
}
static {
// 创建本类的多个实例,并存放到list集合中
for (int i = 0; i < maxCount; i++) {
Multition multition = new Multition();
list.add(multition);
}
}
// 给外界提供一个获取类对象的方法
public static Multition getMultition(){
Random random = new Random();
// 生成一个随机数
int i = random.nextInt(maxCount);
// 从list集合中随机取出一个进行使用
return (Multition)list.get(i);
}
}
4.小结
多例模式可以保证系统中一个类有固定个数的实例, 在实现需求的基础上, 能够提高实例的复用性.
实现多例模式的步骤:
1. 创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
2. 在类中定义该类被创建的总数量
3. 在类中定义存放类实例的list集合
4. 在类中提供静态代码块,在静态代码块中创建类的实例
5. 提供获取类实例的静态方法
三.工厂设计模式
简单工厂模式又称静态工厂方法模式。名上就可以看出这个模式一定很简单。它存在的目的很简单:定义一个用于创建对象的接口。
1.工厂用于造东西的(创建对象),但是不能随便造,不能创造不是一类的东西(对象)
2.所以,为了防止随便造,达成只能创建一类对象的目的,我们可以定义一个抽象类或者接口
3.让创建出来的类去继承抽象类或者实现接口
4.除了子类或者实现类,我们需要一个工厂去专门造对象
5.创建一个工厂类,定义一个方法,到时候调用这个方法的时候,让我创建啥,我就创建啥
6.弊端:
如果调用方法的时候传递了其他的车, createCar()方法就直接报NullPointerException()了
不想报NullPointerException(),就需要在此方法中加一个判断代码,这样会造成我们反复修改源代码的弊端
//抽象产品角色
public interface Car{
public void drive();
}
//具体产品角色
public class Benz implements Car{
public void drive() {
System.out.println("Driving Benz ");
}
}
public class Bmw implements Car{
public void drive() {
System.out.println("Driving Bmw ");
}
}
//工厂角色
public class CarFactory{
//工厂方法.注意 返回类型为抽象产品角色
public static Car createCar(String s) {
//判断逻辑,返回具体的产品角色
if(s.equalsIgnoreCase("Benz"))
return new Benz();
else if(s.equalsIgnoreCase("Bmw"))
return new Bmw();
return null;
}
public class Magnate{
public static void main(String[] args){
Car car = CarFactory.createCar("benz");
car.drive();
}
}
四.工厂方法设计模式
工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
1.问题:
从上面的简单工厂代码来看,如果我们调用createCar,传递的不是Benz和Bmw,那么直接就返回空
再调用drive()方法的时候就报NullPinterException()了
2.工厂方法就是对简单工厂的延伸
3.怎么解决呢?
针对不同的对象,创建不同的工厂.不同的工厂创建不同的对象,我直接不判断了,也就避免了判断有可能失败的问题
4.好处:
a.不用像简单工厂那样,调用方法的时候传递参数而造成NullPinterException()了
b.我们如果传递别的汽车,我就不用像简单工厂那样再加一个判断了(这属于修改源代码,不好)
b.如果我们想创建别的牌子的车,我们直接再创建一个造此车的工厂,不用修改已经写好的代码了
public interface Car{
pubic void driver();
}
public class Benz implements Car{
public void drive() {
System.out.println("Driving Benz ");
}
}
//工厂角色
public interface Factory{
public Car createCar();
}
//具体工厂角色
public class BenzFactory implements Factory{
public Car createCar(){
return new Benz();
}
}
public class BmFactory implements Factory{
public Car createCar() {
return new Bmw();
}
}
public class Magnate{
public static void main(String[] args){
Factory factory = new BenzFactory();
Car car = factory.driverCar();
car.drive();
}
}
五.模版方法设计模式
模板方法(Template Method)模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
饭店中吃饭: 点菜,吃菜和买单三个步骤。点菜和买单基本上一致的,但是吃菜不同,吃法也不同。明确了一部分功能,而另一部分功能不明确。
public abstract class Hotel {
public void eat(){
System.out.println("点菜");
eatCai();
System.out.println("扫码买单");
}
//吃菜的方式
public abstract void eatCai();
}
public class QuanJuDe extends Hotel{
@Override
public void eatCai() {
System.out.println("拿饼");
System.out.println("放鸭肉,葱,黄瓜,甜面酱");
System.out.println("卷");
}
}
public class ZhangLiang extends Hotel{
@Override
public void eatCai() {
System.out.println("麻酱");
System.out.println("小米辣");
System.out.println("香菜");
System.out.println("蒜蓉");
System.out.println("蚝油");
}
}
public class Test {
public static void main(String[] args) {
QuanJuDe quanJuDe = new QuanJuDe();
quanJuDe.eat();
System.out.println("----------------------");
ZhangLiang zhangLiang = new ZhangLiang();
zhangLiang.eat();
}
}
5.总结
动态代理非常的灵活,可以为任意的接口实现类对象做代理
动态代理可以为被代理对象的所有接口的所有方法做代理,动态代理可以在不改变方法源码的情况下,实现对方法功能的增强,
动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。
动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。
动态代理同时也提高了开发效率。
缺点:只能针对接口的实现类做代理对象,普通类是不能做代理对象的。
工厂设计模式:
....
...
....
....
....
面包 mb = new 面包("水","鸡蛋","面粉","酵母","防腐剂","馅儿",new JaoBan(),new RongQi());
mb.set("芝麻");
mb.kaojiao();
类比 面向工厂。
工厂模式使用场景:
1.工厂模式的目的是为了实现解耦,将对象的创建和使用分开,即应用程序将对象的创建和初始化职责交给工厂对象。
若一个对象A想要调用对象B时,如果直接通过new关键字来创建一个B实例,然后调用B实例,这样做的不好处是,当需求变更,要将B实例换成C实例时,
则需要修改所有new了该实例的方法。
2.降低代码重复。如果对象B的创建过程比较复杂,并且很多地方都用到了,那么很可能出现很多重复的代码,通过统一将创建对象B的代码放到工厂里面统一管理,可以减少代码的重复率,同时也方便维护。相比于构造函数来说,复杂的初始化,会使得构造函数非常的复杂。
由于创建过程都由工厂统一的管理,有利于当业务发生变化之后的修改
3.工厂模式将创建和使用分离,使用者不需要知道具体的创建过程,只需要使用即可。
4.类本身有很多子类,并且经常性发生变化。