设计模式
设计模式(Design pattern):代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
JAVA中的设计模式
java的设计模式大体上分为三大类:一共23种
创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。
结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
设计模式遵循的原则有6个:
1、开闭原则(Open Close Principle)
对扩展开放,对修改关闭。
2、里氏代换原则(Liskov Substitution Principle)
只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
3、依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,对接口编程,依赖于抽象而不依赖于具体。
4、接口隔离原则(Interface Segregation Principle)
使用多个隔离的接口来降低耦合度。
5、迪米特法则(最少知道原则)(Demeter Principle)
一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。继承实际上破坏了类的封装性,超类的方法可能会被子类修改。
工厂模型
**工厂模式(Factory Pattern)**是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个 工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:主要解决接口选择的问题。
应用实例:
1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
2、Hibernate 换数据库只需换方言和驱动就可以。
优点:
1、一个调用者想创建一个对象,只要知道其名称就可以了。
2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3、屏蔽产品的具体实现,调用者只关心产品的接口
缺点:
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,
在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事
使用场景:
1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
3、设计一个连接服务器的框架,需要三个协 议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
简单工厂模式:就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例
例子:
package com.rj.bd.factorypattern;
/**
* @desc 面条的抽象类(基类)
* @time 2018-11-9 上午08:24:45
*
*/
public abstract class Noodels {
public abstract void kindsOfNoodle();//面条的种类
}
步骤二:创建实现类:
package com.rj.bd.factorypattern;
/**
* @desc 炒类类
* @time 2018-11-9 上午08:29:08
*
*/
public class ChaoMian extends Noodels {
@Override
public void kindsOfNoodle()
{
System.out.println("此时创造的是:炒面");
}
}
package com.rj.bd.factorypattern;
/**
* @desc 兰州拉面类
* @time 2018-11-9 上午08:27:27
*
*/
public class LanZhouLaMian extends Noodels {
@Override
public void kindsOfNoodle()
{
System.out.println("此时创造的面条为:兰州拉面");
}
}
package com.rj.bd.factorypattern;
/**
* @desc 泡面类
* @time 2018-11-9 上午08:28:25
*
*/
public class PaoMian extends Noodels {
@Override
public void kindsOfNoodle()
{
System.out.println("此时创造的是:泡面");
}
}
步骤三:创建简单工厂对象类
package com.sj.bd.mds;
/**
* @desc 简单工厂类
* @time 2018-08-30
*
*/
public class SimpleNoodlesFactory {
public static final int TYPE_LM = 1;//兰州拉面
public static final int TYPE_PM = 2;//泡面
public static final int TYPE_CM = 3;//炒面
public static Noodles createNoodles(int type) {
case PM:
return new PaoMian();
case LM:
return new LanZhouLaMian();
case CM:
return new ChaoMian();
default:
return new ChaoMian();//默认为炒面类
}
}
步骤四:测试类
package com.sj.bd.mds;
/**
* @desc 测试类:测试简单工厂
* @time 2018-08-30
*
*/
public class CeShi {
public static void main(String[] args) {
Noodels noodel = SimpleNoodelFactory.createNoodels(SimpleNoodelFactory.LM);//此时指出本次创建的面条是哪一类的
noodel.kindsOfNoodle();
}
}
代理模式
1.什么是代理模式:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用
2.好处:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能,换言之:不改变源码的情况下,实现对目标对象的功能扩展
静态代理:所谓静态代理就是自己要为要代理的类写一个代理类,或者用工具为其生成的代理类,总之,就是程序运行前就已经存在的编译好的代理类
特点:不灵活,但是清楚的知道代理类是什么/哪个
例子:
package com.rj.bd.poxystyle.staticproxystyle2;
/**
* @desc 接口
* @time 2018-11-6 上午08:30:47
*
*/
public interface ICar {
public void buyCar();
}
package com.rj.bd.poxystyle.staticproxystyle2;
/**
* @desc 客户类
* @time 2018-11-6 上午08:33:13
*
*/
public class Customer implements ICar {
public int cash;
public String name;
public int getCash() {
return cash;
}
public void setCash(int cash) {
this.cash = cash;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void buyCar() {
System.out.println(this.name+"买车花了:" + this.cash + "元");
}
}
package com.rj.bd.poxystyle.staticproxystyle2;
/**
* @desc 4S店类也就是代理类
* @time 2018-11-6 上午08:36:16
*
*/
public class S4Proxy implements ICar{
public Customer customer;//被代理的对象
public S4Proxy(Customer cu)
{
System.out.println("4S店类初始化,也就是开门正式营业了");
this.customer=cu;//接收买车客户
}
@Override
public void buyCar()
{
int cash = customer.getCash();//先验资,也就是相当于现实世界中的先付款,成功了然后才能说买车成功了
if (cash<100000)
{
System.out.println("您的钱不够:先暂时不能买车呢");
return ;
}
customer.buyCar();
}
}
package com.rj.bd.poxystyle.staticproxystyle2;
/**
* @desc 测试类:测试静态代理模式
* @time 2018-11-6 上午08:40:12
*
*/
public class Test {
public static void main(String[] args)
{
Customer customer=new Customer();
customer.setName("王百万");
customer.setCash(200000);
S4Proxy proxy=new S4Proxy(customer);
proxy.buyCar();
System.out.println("-----------------------------");
Customer customer2=new Customer();
customer2.setName("李美丽");
customer2.setCash(80000);
S4Proxy proxy2=new S4Proxy(customer2);
proxy2.buyCar();
}
}
动态代理:
1.导读模块:相比静态代理,动态代理具有更强的灵活性,因为它不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象,我们可以把这种指定延迟到程序运行时由JVM来实现
2.什么是动态代理:在程序运行时,运用反射机制动态创建而成
导读:在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。
3.动态代理好处
使用Java动态代理机制的好处:
1、减少编程的工作量:假如需要实现多种代理处理逻辑,只要写多个代理处理器就可以了,无需每种方式都写一个代理类。
2、功能的扩展:系统扩展性和维护性增强,程序修改起来也方便多了(一般只要改代理处理器类就行了)。
4.动态代理的使用场景:
1)在后期的商业框架中很多都有用到的,例如Spring,Struts2
JDK动态代理:
JDK动态代理: JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可
简而言之:就是通过绑定接口与实现类,进而实现代理的
步骤:
1)首先,定义业务逻辑接口
2)然后,实现业务逻辑接口创建业务实现类
3)最后,实现 调用管理接口InvocationHandler 创建动态代理(工具)类
4)在使用时,首先创建一个业务实现类对象和一个代理类对象,然后定义接口引用(这里使用向上转型)并用代理对象.bind(业务实现类对象)的返回值进行赋值。最后通过接口引用调用业务方法即可。(接口引用真正指向的是一个绑定了业务类的代理类对象,所以通过接口方法名调用的是被代理的方法们)
例子:
package com.rj.bd.poxystyle.JDKDynamicProxy;
/**
* @desc 接口
* @time 2018-11-6 上午08:30:47
*
*/
public interface ICar {
public void buyCar();
}
package com.rj.bd.poxystyle.JDKDynamicProxy;
/**
* @desc 客户类
* @time 2018-11-6 上午08:33:13
*
*/
public class Customer implements ICar {
public int cash;
public String name;
public int getCash() {
return cash;
}
public void setCash(int cash) {
this.cash = cash;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void buyCar() {
System.out.println(this.name+"买车花了:" + this.cash + "元");
}
}
package com.rj.bd.poxystyle.JDKDynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @desc 动态代理工具类
* @time 2018-11-6 上午09:09:24
*
*/
public class DymaicProxy implements InvocationHandler {
public Object obj;//被代理的对象
public DymaicProxy(Object obj)
{
this.obj = obj;//接受客户
}
/**
* invoke():回调
* 参数介绍:
* 第一个参数:被代理的真实对象
* 第二个参数:代表被动态代理类调用的方法。从中可得到方法名,参数类型,返回类型等等
* 第三个参数:代表被调用方法的参数
* 额外补充:因为是动态代理,所以可以实现一些特殊的功能,可以扩展增强其功能,
* 怎样实现呢:before:dosomething();after:dosomething()
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//before:dosomething();
Object result = method.invoke(this.obj, args);
//after:dosomething()
return result;
}
}
package com.rj.bd.poxystyle.JDKDynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* @desc 测试类:测试JDK动态代理模式
* @time 2018-11-6 上午08:40:12
*
*/
public class Test {
public static void main(String[] args)
{
Customer customer=new Customer();
customer.setName("王百万");
customer.setCash(200000);
//Java反射之getInterfaces()方法:可以获取某一类对像所所实现的接口
//System.out.println(customer.getClass().getInterfaces()[0]);
InvocationHandler handler=new DymaicProxy(customer);
/*
* 通过Proxy类的newProxyInstance()方法来创建我们的代理对象,我们来看看其三个参数
* 第一个参数:类加载器,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
* 第二个参数:对象要实现哪些接口:我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
* 第三个参数:它是一个接口!它的名字叫调用处理器,使用新建的proxy对象调用方法的时候,都会调用到该接口中的invoke方法。 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
*/
ICar car = (ICar) Proxy.newProxyInstance(handler.getClass().getClassLoader(), customer.getClass().getInterfaces(), handler);
System.out.println("handler.getClass().getClassLoader()---->"+handler.getClass().getClassLoader());//获取到JVM创建的相当于是4S店的代理类
System.out.println("cu.getClass().getInterfaces()---->"+cu.getClass().getInterfaces()[0]);//获取到Icar接口
System.out.println("handler---->"+handler);//通过句柄可以间接的获取到被代理类,即客户类
car.buyCar();
}
}
CGLIB动态代理:
cglib代理:cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理
简而言之:对指定的目标类生成一个子类,并覆盖其中方法实现增强,进行代理的
备注:要想使用cglib代表,则必须额外的引入新的jar包:asm和cglib
PS:sping-core.jar包含了以上二者
例子:
package com.rj.bd.poxystyle.CglibDynamicProxy;
/**
* @desc 客户类:目标对象,没有实现任何接口
* @time 2018-11-6 上午08:33:13
*
*/
public class Customer {
public int cash;
public String name;
public int getCash() {
return cash;
}
public void setCash(int cash) {
this.cash = cash;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void buyCar() {
System.out.println(this.name+"买车花了:" + this.cash + "元");
}
}
package com.rj.bd.poxystyle.CglibDynamicProxy;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
* @desc 代理类
* @time 2018-11-6 下午01:47:02
*
*/
public class CglibProxy implements MethodInterceptor {
public Object target;//目标对象
//给目标对象创建一个代理对象
public Object getProxyInstance(Object target){
this.target = target;
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override//给目标对象创建一个代理对象之前实现方法增强
public Object intercept(Object arg0, Method method, Object[] arg2,
MethodProxy arg3) throws Throwable
{
System.out.println("向观众问好");
//执行目标对象的方法
Object returnValue = method.invoke(target, arg2);
System.out.println("谢谢大家");
return returnValue;
}
}
package com.rj.bd.poxystyle.CglibDynamicProxy;
/**
* @desc 测试类:测试Cglib代理
* @time 2018-11-6 下午01:56:06
*
*/
public class Test {
public static void main(String[] args) {
//1.初始化客户对象,并且给对象中的变量赋值
Customer target=new Customer();
target.setName("王百万");
target.setCash(200000);
//2.初始化代理类:目的是将
CglibProxy cglib=new CglibProxy();
Customer bookCglib=(Customer)cglib.getProxyInstance(target);
bookCglib.buyCar();
}
}
单例模式
导读模块:单例模式确保某个类只有一个实例,而且自动实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler(打印区),以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头
1.单例:保证一个类仅有一个实例,并提供一个访问它的全局访问点
2.单例模式有一下特点:
1、单例类只能有一个实例。
2、单例类必须自创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例
3.单例模式的优缺点
优点:该类只存在一个实例,节省系统资源;对于需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
缺点:不能外部实例化(new),调用人员不清楚调用哪个方法获取实例时会感到迷惑,尤其当看不到源代码时
4.使用场景:
1)在后续的框架中:例如Spring框架
2)例如产生:商品订单号,保险单号
懒汉式:在懒汉式中存在不安全。使用双重加锁机制更好的实现懒汉式单例
实例:
package com.rj.bd.poxystyle.Singletons.lazymstyle;
/**
* @desc 懒汉式应用之商品订单类
* @time 2018-11-8 下午02:14:30
*
*/
public class GoodsNum {
private static GoodsNum goodsNum;
public static int i =1;
public GoodsNum() {
}
public static GoodsNum getInstance()
{
if (goodsNum==null)
{
synchronized(GoodsNum.class)
{
if (goodsNum==null)
{
goodsNum=new GoodsNum();
}
i++;
}
}
System.out.println("同步锁的判断次数"+i);
return goodsNum;
}
}
饿汉式单例:类加载的时候就初始化实例
例子:
package com.rj.bd.poxystyle.Singletons.hungrystyle;
/**
* @desc 饿汉式
* @time 2018-11-8 上午11:39:27
*
*/
public class GoodsNum {
private static GoodsNum goodsNum = new GoodsNum();
private GoodsNum()
{
}
/**
* @desc 初始化类对象
* @return
*/
public static GoodsNum getInstance() {
return goodsNum;
}
}
package com.rj.bd.poxystyle.Singletons.hungrystyle;
/**
* @desc 用户的订单模块
* @time 2018-11-8 下午02:39:35
*
*/
public class OrderThread implements Runnable{
@Override
public void run()
{
GoodsNum onlyOnGoodsNum = GoodsNum.getInstance();
System.out.println(onlyOnGoodsNum.hashCode());
}
}
package com.rj.bd.poxystyle.Singletons.hungrystyle;
/**
* @desc 测试:测试懒汉式
* @time 2018-11-8 下午02:22:28
*
*/
public class Test {
public static void main(String[] args) {
OrderThread orderThread=new OrderThread();
/* Thread thread1=new Thread(orderThread);
Thread thread2=new Thread(orderThread);
thread1.start();
thread2.start();*/
Thread []threads=new Thread[10000];
for (int i = 0; i <threads.length; i++)
{
threads[i]=new Thread(orderThread);
threads[i].start();
}
}
}
策略模式
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。
**意图:**定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
**主要解决:**在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
**何时使用:**一个系统有许多许多类,而区分它们的只是他们直接的行为。
**如何解决:**将这些算法封装成一个一个的类,任意地替换。
**关键代码:**实现同一个接口。
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
实现:
/**
*
* @ClassName: MethodOfTravel
* @Description: 定义抽象旅行方式
* @date: 2019年4月11日 上午10:56:56
*/
public interface MethodOfTravel {
public void Method();
}
/**
*
* @ClassName: Airplane
* @Description: 坐飞机
* @date: 2019年4月11日 上午10:58:43
*/
public class Airplane implements MethodOfTravel {
@Override
public void Method() {
System.out.println("选择坐飞机的方式旅游");
}
}
/**
*
* @ClassName: Train
* @Description: 坐火车
* @date: 2019年4月11日 上午11:07:29
*/
public class Train implements MethodOfTravel {
@Override
public void Method() {
System.out.println("选择坐火车的方式旅游");
}
}
/**
*
* @ClassName: TravelContext
* @Description: 环境类
* @date: 2019年4月11日 上午11:08:23
*/
public class TravelContext {
private MethodOfTravel methodOfTravel;
public MethodOfTravel getMethodOfTravel() {
return methodOfTravel;
}
public void setMethodOfTravel(MethodOfTravel methodOfTravel) {
this.methodOfTravel = methodOfTravel;
}
/**
*
* @Title: method
* @Description: 旅游方式
* @return: void
*/
public void method(){
methodOfTravel.Method();
}
}
public class StrategyTest {
public static void main(String[] args) {
MethodOfTravel airplane = new Airplane();
MethodOfTravel train = new Train();
TravelContext context = new TravelContext();
context.setMethodOfTravel(airplane);
context.method();
System.out.println("================================");
context.setMethodOfTravel(train);
context.method();
}
}