工厂模式
工厂模式目的是将创建对象的具体过程屏蔽隔离起来,从而达到更高的灵活性,工厂模式分为三类: 简单工厂模式 || 工厂方法模式 || 抽象工厂模式
没有工厂模式的情况
用户需要知道怎么创建需要的物品,会使得耦合性变高,为了降低耦合就出现了简单工厂模式,把创建的操作细节放到工厂里,而客户直接使用工厂创建方法,只需要传入想要的物品,不必在意更多细节
public class A{
public A(){
sout("制造a")
}
}
public class B{
public B(){
sout("制造b")
}
}
public class c{
public static void main(String[] args){
A a= new A();
B b= new B();
}
}
简单工厂模式
简单工厂模式的核心是定义一个创建对象的接口,将对象的创建和本身的业务逻辑分离,降低系统耦合度,使两个修改起来相对容易,以后实现改变只需要修改工厂类
工厂类对象:该模式核心,用来创建产品,含有一定商业逻辑和判断逻辑
抽象产品角色:一般是具体产品继承的父类或实现接口
具体产品角色:工厂类创建对象就是此角色的实例,在java中由一个具体类实现
优点:简单工厂模式提供专门的工厂类创建对象,实现对象创建和使用的职责分离,客户端不用知道创建的具体产品类的类名以及创建过程,只要知道具体产品类所对应的参数即可,通过引入配置文件可以在不修改客户端代码的情况,更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
缺点:不符合“开闭原则”,每次添加新产品都需要修改工厂类,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统扩展维护,并且工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响
为了解决这个问题,出现了工厂方法模式
//产品类
abstract class A{
public A(){}
}
public class Aa extends A{
public Aa(){
sout("制造Aa");
}
}
public class Ab extends A{
public Ab(){
sout("制造Ab");
}
}
//工厂类
public class B{
public A createA(String type){
if(type.equals("Aa")){
return new Aa();
}
if(type.equals("Ab")){
return new Ab();
}
return null
}
}
//用户类
public class C{
public static void main(String[] args){
B b = new B();
A aa = B.createA("Aa"); //用户传入一个对应工厂类的值,会创建相对应的产品类
A ab = B.createA("Ab");
}
}
工厂方法模式
工厂方法模式将工厂抽象化,定义一个创建对象的接口,增加新的产品只需增加产品以及对应的具体实现工厂类,由具体工厂类决定要实例化哪个产品,将对象创建与实例化延迟到子类,这样工厂设计就符合开闭原则了,扩展时不必去修改原来的代码。
但缺点是每增加一个产品都需要增加一个具体产品类和实现工厂类,使系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时增加了系统具体类的依赖。
抽象工厂:工厂方法模式的核心,是具体工厂角色必须实现的接口或必须继承的父类,在java中由抽象类或接口实现
具体工厂:被应用程序调用以创建具体产品对象,含有和具体业务逻辑有关的代码
抽象产品:是具体产品继承的父类或实现的接口,在java中一般有抽象类或接口来实现
具体产品:具体工厂角色所创建的对象就是此角色的实例
//产品类
abstract class A{
public A(){}
}
public class Aa extends A{
public Aa(){
sout("制造Aa")
}
}
public class Ab extends A{
public Ab(){
sout("制造Ab")
}
}
//工厂类
interface B{
A createA();
}
public class Ba implements B{
public Aa createA(){
return new Aa();
}
}
public class Bb implements B{
public Ab createA(){
return new Ab();
}
}
//客户类
public class C{
public static void main(String[] args){
Ba ba = new Ba(); //通过新建一个对应工厂类,使用该工厂类创建该工厂生产的产品类
Aa a = ba.createA();
Bb bb = new Bb();
Ab b = bb.createA();
}
}
抽象工厂模式
在工厂方法模式中。使用一个工厂创建一个产品,一个具体工厂对应一个具体产品,但有时候需要一个工厂能够提供多个产品对象,而不是单一对象,这个时候就需要使用哦更抽象工厂模式
抽象工厂:定义一个接口,这个接口包含一组方法用来生产产品,所有具体工厂都必须实现此接口
具体工厂:用于生产不同产品族,要创建一个产品,用户只需使用其中一个工厂进行获取,完全不需要实例化任何产品对象
抽象产品:是一个产品家族,每一个具体工厂都能生产一整组产品
//产品类--配件创建
public interface E{}
public class Ea implements E{
public Ea(){
sout("制造a");
}
}
public class Eb implements E{
public Eb(){
sout("制造b");
}
}
//创建工厂类
public interface A{
//制造配件
public E createE();
}
//为A产品生产配件
public class Fa implements A{
public E createE(){
return new Ea();
}
}
//为B产品生产配件
public class Fb implements A{
public E createE(){
return new Eb();
}
}
//客户
public class C{
public static void main(String[] args){
//生产A产品和对应配件
Fa fa = new Fa();
fa.createE();
}
//生产B产品和对应配件
Fb fb = new Fb();
fb.createE();
}
工厂方法模式和抽象工厂模式的区别
工厂方法只有一个抽象产品类和一个抽象工厂类,但可以派生出多个具体产品类和具体工厂类,每个具体工厂类只能创建一个具体产品类的实例
抽象工厂模式有多个抽象产品类和一个抽象工厂类,每个抽象产品类可以派生出多个具体产品类,抽象工厂类也可以派生多个具体工厂类,同时每个具体工厂类可以创建多个具体产品类的实例
适配器模式
适配器模式把一个类的接口换成客户端期待的另一种接口,使原本因接口不匹配而无法在一起工作的两个类能够在一起工作
适配器分为类适配器和对象适配器两种模式
类适配器
让Adapter继承Adaptee类,然后再实现Target接口,实现适配器功能
优点:由于Adapter继承Adaptee类,所以可以根据需求重写Adaptee类的方法,使Adapter的灵活性增强
缺点:因为java单继承,Target必须是接口,以便于Adapter去继承Adaptee并实现Target,完成适配功能,但这样就会导致Adapter里暴露了Adaptee类的方法,使用起来的成本就增加了
//源角色-Adaptee:需要适配的接口
public class A{
public int A220(){
int a = 220;
return a;
}
}
//目标角色-Target:这就是所期待得到的接口;由于讨论的是类适配器模式,因此目标不可以是类
public interface D{
int A5();
}
//类适配器:适配器是本模式的核心,适配器把原接口转换成目标接口,相对应的这一角色不可以是接口,必须是具体类。
public class P extends A220 implements D{
public int A5(){
int a = A220();
return(a/44);
}
}
//测试类
public class T{
main(){
D d =new P();
sout("输出电流:"+D.A5())
}
}
对象适配器
让Adapter持有Adaptee类的实例,然后再实现Target接口,以这种持有对象的方式来实现适配器功能
优点:根据合成复用原则,使用组合替代继承,解决了类适配器必须继承Adaptee的局限性问题,也不要求Target必须是接口,使用成本降低,更加灵活
//源角色-Adaptee:需要适配接口
public class A220(){
public int o220(){
int o = 220;
return o;
}
}
//目标角色-Target:这就是所期待得到的接口
public interface D{
int o5();
}
//对象适配器
public class P implements D{
private A220 a220;
public P(A220 a220){
this.a220 = a220;
}
public int o5(){
int o = this.a220.o220();
return(o/44);
}
}
//测试对象适配器
public class Test{
public static void main(String[] args){
A220 a220 = new A220();
P p = new p(a220);
sout("输出电流:"+p.o5);
}
}
代理模式
定义:对其他对象提供代理以控制对这个对象的访问
作用
代理模式主要是为其他对象提供一种代理以控制对这个对象的访问,在某些情况下一个对象不想或不能直接引用另一个对象,而代理对象可以在客户端和目标对象间起到中介作用。
代理模式就是为了提供额外的处理或不同的操作而在实际对象与调用者间插入一个代理对象,这些额外的操作通常与实际对象进行通信
代理模式是一种设计模式,就是在不改变源码的情况下,实现对目标对象的功能扩展
抽象角色:通过接口或抽象类声明真实角色实现的业务方法
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑实现抽象方法,并可以附加自己的操作
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,以便代理角色调用
public class mind{
public void m(){
sout("中间代码");
}
}
//假设想在中间代码前后分别添加语句,即是对该方法进行扩展
public void m(){
sout("第一句");
sout("中间代码");
sout("第三句");
} //但是通常情况下又不能直接对源代码进行修改,所以就会产生代理模式(静态代理 || 动态代理)
代理模式分类
分为静态代理 || 动态代理(JDK) || Cglib代理
三种代理模式各有各的优缺点和适用范围,主要看目标对象是否实现接口。
在Spring的AOP编程中: 如果加入容器的目标对象有实现接口,用JDK代理 如果目标对象没有实现接口,用Cglib代理
静态代理
静态代理就是写死了代理对象中执行这个方法前后执行添加功能的形式,每次要在接口中添加新方法,则需要在目标对象中实现该方法,并在代理对象中实现对应的代理方法
缺点:这种方式虽然直观简单,但是代理对象必须提前写出,如果接口层发生变化,代理对象代码也要进行维护,如果能在运行时动态的写出代理对象,不但减少大批代理类的代码,也少了不断维护的烦恼,但是运行效率也会受到影响
public interface I{
void s();
}
//目标对象实现某一接口
public class Se implements I{
public void s(){
sout("中间段语句");
}
}
//代理对象和目标对象实现相同接口
public class Sp implements I{
private I target;
//接收目标对象,以便调用s方法
public Ud(I target){
this.target = target;
}
//对目标对象的s方法进行功能扩展
public void s(){
sout("第一句");
target.s();
sout("第三句");
}
}
//测试类
public class Test{
public static void main(String[] args){
//目标对象
I i = new Se();
//代理对象
I p = new Sp(i);
//执行代理方法
p.s();
}
}
动态代理(JDK代理)
动态代理是在程序运行时通过反射机制动态创建的代理类
Proxy类:创建代理角色的方法。
InvocationHandler接口:提供的执行被代理角色方法
由于java底层封装了实现细节,所以代码非常简单,格式也基本定型
只需调用Proxy类的静态方法newProxyInstance,该方法会返回代理类对象
缺点:和静态代理有一个共同缺点就是目标对象必须实现一个或多个接口,如果没有则使用Cglib代理
public interface I{
void s();
}
//目标对象实现了某一接口
public class Se implements I{
public void s(){
sout("中间代码段");
}
}
//测试类
public class Test{
main(){
Se t = new Se();
I p = (I) Proxy.newProxyInstance(
t.getClass().getClassLoader(),
t.getClass().getInterfaces(),
new IH(){
public Object invoke(Object p,Method m,Object[] args) throws Throwable{
sout("第一句");
Object r = m.invoke(t,args);
sout("第三句");
return r;
}
});
p.s();
)
}
}
Cglib代理
前提条件:
需要引入cglib的jar文件,由于spring的核心包已经包括该功能,所以可以直接引入spring-core
目标类不能为final
目标对象的方法如果为final || static,那么就不会拦截,即不会执行目标对象额外的业务方法
public class Se{
public void s(){
sout("中间代码块")
}
}
//Cglib子类代理工厂
public class P implements MethodInterceptor{
//维护目标对象
private Object t;
public P(Object t){
this.t = t;
}
//给目标对象创建一个代理对象
public Object getP(){
//工具类
Enhancer e = new Enhancer();
//设置父类
e.setSuperclass(t.getclass());
//设置回调函数
e.setCallback(this);
//创建代理对象
return e.create();
}
public Object i(Object o, Method m, Object[] args,MethodProxy p)throws Throwable{
sout("第一句");
Object r = method.invoke(t,args);
sout("第三句");
return r;
}
}
//测试类
public class Test{
public static void main(String[] args){
Se t = new Se(); //目标对象
Se p = (Se)new ProxyFactory(t).getProxyInstance(); //代理对象
p.s(); //执行代理对象的方法
}
}