设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
总而言之,设计模式就是各个大佬在开发中总结出来的能很好解决问题的方式。
总体来说设计模式分为三大类:
1.创建型模式:包括工厂模式、抽象工厂模式、单例模式、建造者模式、原型模式
2.结构型模式:包括适配器模式、桥接模式、过滤器模式、组合模式、装饰器模式、外观模式、享元模式、代理模式
3.行为型模式:包括责任链模式(、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、空对象模式、策略模式、模板模式、访问者模式
4:J2EE 模式:包括MVC 模式、业务代表模式、组合实体模式、数据访问对象模式、前端控制器模式、拦截过滤器模式、服务定位器模式、传输对象模式
这里先介绍前三种中常见的设计模式:单例模式、工厂模式、建造者模式、适配器模式、装饰模式、策略模式、观察者模式、代理模式。
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。顾名思义,他的意思就是一个类就只能有一个唯一的实例,需要该类的实例时,是无法像平常那样new出来的,因为他的构造方法是private的,他的实例只会new一次,之后再使用该类的实例时都不会在new了。
单例模式的实现可以分为饿汉式和懒汉式,具体实现如下:
public class EhSingleton {
public static EhSingleton instance = new EhSingleton();
private EhSingleton() {
}
public static EhSingleton getInstance() {
return instance;
}
public class LhSingleton {
public static volatile LhSingleton instance;
private LhSingleton() {
}
public static LhSingleton getInstance() {
if (instance == null) {
synchronized (LhSingleton.class) {
if (instance == null) {
instance = new LhSingleton();
}
}
}
return instance;
}
}
可以看出,饿汉式是在类加载的时候就把实例对象给创建了,而懒汉式则是在第一次使用到的时候把它创建出来。可以根据实际的需要选择对应的方式。
工厂模式简单的说就是对象通过工厂来生产出来,而不需要自己去new,只需要知道要创建的是什么对象,之后去工厂提出来就可以了。
实现:有一个接口A,它有A1和A2两个实现类,可以使用工厂根据名称来获取A的对应的对象。
/*******************************接口A****************************/
public interface A {
void method();
}
/*******************************实现类A1****************************/
public class A1 implements A{
@Override
public void method() {
System.out.println("this is A1");
}
}
/*******************************实现类A2****************************/
public class A2 implements A {
@Override
public void method() {
System.out.println("this is A2");
}
}
/*******************************工厂****************************/
public class NormalFactory {
public A bulid(String type) {
if ("A1".equals(type)) {
return new A1();
} else if ("A2".equals(type)) {
return new A2();
}else {
System.out.println("没有对应的实现");
return null;
}
}
}
/*****************************使用*******************************/
public static void main(String[] args) {
//普通的工厂:
NormalFactory normalFactory = new NormalFactory();
A a1 = normalFactory.bulid("A1");
a1.method();
A a2 = normalFactory.bulid("A2");
a2.method();
A a3 = normalFactory.bulid("A3");
}
输出如下:
在上面例子中可以看到,如果传递的参数错误的时候就不能正确的把对象创建出来了,如果使用多个工厂的模式(提供多个工厂方法),分别来创建对象就不用担心了。
/****************************A A1 A2 同上***************************/
/****************************多个工厂类******************************/
public class MultiFactory {
public A bulidA1() {
return new A1();
}
public A bulidA2() {
return new A2();
}
}
/*******************************实现***********************************/
public static void main(String[] args) {
MultiFactory multiFactory = new MultiFactory();
A m1 = multiFactory.buildA1();
m1.method();
A m2 = multiFactory.buildA2();
m2.method();
}
这样直接创建指定的对象就没有传错参数的烦恼了。
静态工厂方法模式:将上述的工厂方法设为静态的,就不需要创建工厂的实例了。
在上述的工厂模式中,如果又添加了个实现类,就需要对工厂类进行修改,比较麻烦。所以可以把工厂给抽象出来,一个实现类对应一个工厂的实现类,再添加新的实现类时,只需要新写一个工厂实现类即可。
实现:
/********************************A A1 A2 同上**********************************/
/*******************************工厂的抽象类***********************************/
public interface Provider {
A bulid();
}
/******************************A1的工厂实现类***********************************/
public class A1Factory implements Provider {
@Override
public A bulid() {
return new A1();
}
}
/******************************A2的工厂实现类***********************************/
public class A2Factory implements Provider {
@Override
public A bulid() {
return new A2();
}
}
/*********************************使用******************************************/
public static void main(String[] args) {
Provider provider1 = new A1Factory();
A p1 = provider1.bulid();
p1.method();
Provider provider2 = new A2Factory();
A p2 = provider2.bulid();
p2.method();
}
当要创建一个过于复杂的对象时候,使用建造者模式可以把它分解成多个简单的对象一步一步的构建起来。
比如一个对象它有ABC3个复杂的模块,创建起来就比较复杂;或者对象可能只需要AC模块,可能全部都需要;或者对象要先创造B模块再创造A模块...建造者模式就是将这3个模块分离开,单独创建,在根据具体的需求/顺序来将这些模块组合成完整的对象。
实现:
public class Model {
private String A;
private String B;
private String C;
public String getA() {
return A;
}
public void setA(String a) {
A = a;
}
public String getB() {
return B;
}
public void setB(String b) {
B = b;
}
public String getC() {
return C;
}
public void setC(String c) {
C = c;
}
@Override
public String toString() {
return "Model{" +
"A='" + A + '\'' +
", B='" + B + '\'' +
", C='" + C + '\'' +
'}';
}
}
public interface ModelBuilder {
void buildA();
void buildB();
void buildC();
Model buildModel();
}
public class BuildMethod implements ModelBuilder {
private Model model;
public BuildMethod() {
model = new Model();
}
@Override
public void buildA() {
model.setA("A");
System.out.println("buildA");
}
@Override
public void buildB() {
model.setB("B");
System.out.println("buildB");
}
@Override
public void buildC() {
model.setC("C");
System.out.println("buildC");
}
@Override
public Model buildModel() {
return model;
}
}
public class ModelConstruct {
//只构建AC
public Model buildAC(ModelBuilder builder) {
builder.buildA();
builder.buildC();
return builder.buildModel();
}
//全部构建
public Model buildABC(ModelBuilder builder) {
builder.buildA();
builder.buildB();
builder.buildC();
return builder.buildModel();
}
}
public static void main(String[] args) {
ModelConstruct mc = new ModelConstruct();
Model m1 = mc.buildAC(new BuildMethod());
System.out.println(m1);
Model m2 = mc.buildABC(new BuildMethod());
System.out.println(m2);
}
结果如下:
适配器模式简单的说就是,类的接口不符合自己的需求,在不改变类的基础上,增加一个适配器,转成自己需要的。就像插头是2孔,但是插座是3孔的,这时候就需要一个“适配器”,把2孔的插头转换成3孔的就能插进去了。
适配器模式分为3类:类的适配器、对象的适配器、接口的适配器
/********************实际需要method1、method2两个方法********************/
public interface Target {
void method1();
void method2();
}
/**********************但原有的类只有method1***********************/
public class Source {
public void method1() {
System.out.println("this is method1");
}
}
/*********************需要个适配器增加method2的方法********************/
public class ClassAdapater extends Source implements Target{
@Override
public void method2() {
System.out.println("this is method2");
}
}
/************这样在ClassAdapater里method1、method2都有了************/
/***************************使用******************************/
public static void main(String[] args) {
Target target = new ClassAdapater();
target.method1();
target.method2();
}
与类的适配器思路相同,只是不继承Source类,而是Source类的实例来实现method1.
/*********************Target和Sourcetoo同上*****************************/
public class ObjectAdapter implements Target {
private Source source;
public ObjectAdapter(Source source) {
this.source = source;
}
@Override
public void method1() {
source.method1();
}
@Override
public void method2() {
System.out.println("this is method2");
}
}
/*******************使用**************************/
public static void main(String[] args) {
Source source = new Source();
Target target1 = new ObjectAdapter(source);
target1.method1();
target.method2();
}
有时候一个接口有很多的抽象方法,写实现类时就要把他们全部实现,有时并不需要使用全部的接口。接口的适配器就是:创建一个抽象类来实现这些接口,当我们使用时,只需要继承这个抽象类,然后重写我们需要的方法就可以。
顾名思义,就是给一个对象增加的方法增加一些新的功能做装饰。比如要对A对象的F方法在调用时添加打印helloWord的功能,其他对象没有。只需要装饰者和A都同一个接口,装饰者拥有A的实例并将该接口重写即可。
实现:
/***************************被装饰的类*************************/
public class Source implements Target{
public void method() {
System.out.println("method!!!");
}
}
/*************************需要被装饰的方法的接口********************/
public interface Target {
void method();
}
/************************装饰者************************/
public class Decorator implements Target {
private Target source;
public Decorator(Target source) {
this.source = source;
}
@Override
public void method() {
System.out.println("hello world!!!!");
source.method();
}
}
/****************使用时,把需要装饰的对象传给装饰者装饰***************/
public static void main(String[] args) {
Target source = new Source();
Target target = new Decorator(source);
target.method();
}
结果:
简单来说,就是多个方法都去实现一个统一的方法。策略模式提供了这个方法的不同实现,在使用的时候只需要决定要那个实现即可。
/***********************统一的计算方法******************/
public interface Strategy {
int cal(int x,int y);
}
/*********************加法的实现*******************/
public class Add implements Strategy {
@Override
public int cal(int x, int y) {
return x + y;
}
}
/*********************减法的实现*******************/
public class Sub implements Strategy {
@Override
public int cal(int x, int y) {
return x - y;
}
}
/********************应用的场景-计算器*****************/
public class Calculator {
public int doCalc(int x, int y, Strategy strategy) {
return strategy.cal(x, y);
}
}
/***********************使用**************************/
public static void main(String[] args) {
Strategy add = new Add();
Strategy sub = new Sub();
int x = 9;
int y = 3;
Calculator calc = new Calculator();
int addRes = calc.doCalc(x,y,add);
System.out.println(x + "+" + y + "=" + addRes);
int subRes = calc.doCalc(x,y,sub);
System.out.println(x + "-" + y + "=" + subRes);
}
观察者模式就是当一个对象有变化时,其他和它有关联的对象都会收到消息,然后发生相应的变化。
/**************************观察者**************************/
public interface Observer {
void update();
}
/*************************发生变化的主体*****************/
public class Subject {
//依赖的观察者
private List observers = new ArrayList<>();
public void change() {
System.out.println("subject has been change!");
//通知给观察者
notifyObserver();
}
public void addObserver(Observer observer){
//添加观察者
observers.add(observer);
}
private void notifyObserver() {
for (Observer o :observers) {
//观察者执行相应的方法
o.update();
}
}
}
/************************具体的观察者A****************************/
public class ObserverA implements Observer {
@Override
public void update() {
System.out.println("ObserverA notify!");
}
}
/************************具体的观察者B****************************/
public class ObserverB implements Observer {
@Override
public void update() {
System.out.println("ObserverB notify!");
}
}
/***********************使用****************************/
public static void main(String[] args) {
Subject subject = new Subject();
Observer observerA = new ObserverA();
Observer observerB = new ObserverB();
subject.addObserver(observerA);
subject.addObserver(observerB);
subject.change();
}
代理模式就是不直接使用实现这个功能的对象,或者访问这个对象有困难,访问的时候使用另一个对象,而不是直接实现功能的对象。就像不想去太远的火车站买车票,而选择去火车站代售点买车票。
/**************************要实现的功能****************/
public interface Book {
void read();
void write();
}
/************************真正实现功能的类***************/
public class RealBook implements Book{
@Override
public void read() {
System.out.println("read book!");
}
@Override
public void write() {
System.out.println("write book!");
}
}
/*********************代理类**********************/
public class ProxyBook implements Book {
private RealBook realBook;
@Override
public void read() {
if (realBook == null) {
realBook = new RealBook();
}
realBook.read();
}
@Override
public void write() {
if (realBook == null) {
realBook = new RealBook();
}
realBook.write();
}
}
/*********************使用*********************/
public static void main(String[] args) {
Book book = new ProxyBook();
book.read();
book.write();
}
可以看到调用book的read和write方法的是ProxyBook,但ProxyBook其实是用realBOOK来实现。
接口的方法有很多的话,就要编写很多的实现方法,而且没当新增一个方法,实现类也要跟着添加,就比较麻烦,而使用动态代理就方便很多。动态代理的实现方式,是通过反射来实现的,借助Java自带的java.lang.reflect.Proxy
,通过固定的规则生成。
实现:
/*******************book和realbook同上*********************/
/**********************动态代理类*******************/
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(object,args);
}
}
/*****************************使用*****************************/
public static void main(String[] args) {
Book realBook = new RealBook();
DynamicProxy proxy = new DynamicProxy(realBook);
ClassLoader classLoader = realBook.getClass().getClassLoader();
Book proxyBook = (Book) Proxy.newProxyInstance(classLoader, new Class[]{Book.class}, proxy);
proxyBook.write();
proxyBook.read();
}
对于常用的设计模式,我理解的就是这些啦。学习了之后,发现设计模式在平时还挺多的,像spring里就用了很多,比如是单例、工厂、代理等等,理解设计模式之后看源码也容易多了。
上述例子全部代码:https://github.com/fi00wind/design-pattern