java设计模式总结1
文章目录
1.工厂设计模式
概念
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
按细的分一共有三种,一种是简单工厂,一种是工厂方法还有就是抽象工厂。GOF在《设计模式》一书中将工厂模式分为两类:
工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。
将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。
1.1简单工厂的实现方式https://blog.csdn.net/u011024652/article/details/80917025
简单的说就是讲对象创建从单独剥离出来放到一个工厂中,专门用这个工厂去创建对象
public class PizzaStore {
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public void orderPizza(String type) {
Pizza pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}
1.2工厂方法
工厂方法模式 定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
public abstract class PizzaStore {
public abstract Pizza createPizza(String type);
public void orderPizza(String type) {
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}
}
/**纽约分店*/ //更具分店重写这个抽象方法去实现这个抽象方法,然后在去调用这个抽象方法
public class NYPizzaStore extends PizzaStore {
@Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if("cheese".equals(type)) {
pizza = new NYCheesePizza();
} else if("clam".equals(type)) {
pizza = new NYClamPizza();
} else if("veggie".equals(type)) {
pizza = new NYVeggiePizza();
}
return pizza;
}
}
/**芝加哥分店*/
public class ChicagoPizzaStore extends PizzaStore {
@Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if("cheese".equals(type)) {
pizza = new ChicagoCheesePizza();
} else if("clam".equals(type)) {
pizza = new ChicagoClamPizza();
} else if("veggie".equals(type)) {
pizza = new ChicagoVeggiePizza();
}
return pizza;
}
}
在上面的定义中讲到,工厂方法模式让子类决定要实例化的类是哪一个,这里的决定并不是允许子类在运行时做决定,而是在编写创建者类时,不需要知道实际创建的产品是哪一个,选择了使用哪个子类,自然就决定了实际创建的产品是什么。工厂方法模式通过让子类决定创建的对象是什么,来达到将对象创建的过程封装的目的。
工厂方法模式帮助我们将产品的“实现”从“使用”中解耦,如果增加或改变产品,产品的“使用”将不会受到影响。下面的类图就描述了工厂方法模式
1.3抽象工厂模式的定义
抽象工厂模式 提供了一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
工厂模式步骤总结:(我的理解就是总而言之就是一个用来创建对象的工厂)
简单工厂模式
步骤一:定义一个接口或者抽象类
步骤二:实现这个接口或者抽象类
步骤三:创建一个工厂,基于给定的信息的
步骤四:使用该工厂,通过传递信息来获取实体类对象
工厂方法模式:(重写的都是同一个方法,然后在自己实现,根据传递什么就实现什么)
步骤一:定义一个接口或者抽象类
步骤二:实现这个接口或者抽象类
步骤三:创建一个抽象工厂,在这个抽象工厂中创建一个方法
步骤四:创建工厂方法的子类,然后重写这个抽象类里面的抽象方法
步骤五:使用该工厂,通过传递信息来获取实体类对象
public abstract class Product { //产品抽象类
public abstract void method();
}
public class ConcreteProductA extends Product { //具体A产品
@Override
public void method() {
System.out.println("产品A");
}
}
public class ConcreteProductB extends Product { //具体B产品
@Override
public void method() {
System.out.println("产品B");
}
}
public abstract class Factory { //工厂抽象类
public abstract Product createProduct();
}
public class ConcreteFactory extends Factory { //具体工厂
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
public class Client { //客户端使用
public static void main(String[] args) {
Factory factory = new ConcreteFactory();
Product product = factory.createProduct();
product.method();
}
}
抽象工厂模式(重写的都是不同方法,然后在自己实现,要什么功能就调用是功能)
步骤一:定义一个接口或者抽象类
步骤二:实现这个接口或者抽象类
步骤三:创建一个抽象工厂(有很多的抽象方法)
步骤四:创建一个工厂,(实现抽象工厂的方法)
步骤五:使用该工厂,通过传递信息来获取实体类对象(此时在这个就可以使用多态了)
编译看左,运行看右,例子:基类 对象名= new 实际调用的类();
对于工厂方法举个例子
网络例子
//1.抽象工厂
public abstract class CarFactory {
/**
* 生产轮胎
*
* @return 轮胎
* */
public abstract ITire createTire();
/**
* 生产发动机
*
* @return 发动机
* */
public abstract IEngine createEngine();
/**
* 生产制动系统
*
* @return 制动系统
* */
public abstract IBrake createBrake();
}
//2.三个产品抽象接口
public interface ITire {
/**
* 轮胎
*/
void tire();
}
public interface IEngine {
/**
*发动机
*/
void engine();
}
public interface IBrake {
/**
*制动系统
*/
void brake();
}
//3.根据抽象接口定义不同的对象
public class NormalBrake implements IBrake{
@Override
public void brake() {
System.out.println("普通制动");
}
}
public class SeniorBrake implements IBrake{
@Override
public void brake() {
System.out.println("高级制动");
}
} //后面的定义省略。。。。。。。。。。。。。
//4.实现具体的工厂类
public class Q3Factory extends CarFactory{
@Override
public ITire createTire() {
return new NormalTire();
}
@Override
public IEngine createEngine() {
return new DomesticEngine();
}
@Override
public IBrake createBrake() {
return new NormalBrake();
}
}
//5.客户端使用
public class Client {
public static void main(String[] args) {
//A车厂
CarFactory factoryQ3 = new Q3Factory();
factoryQ3.createTire().tire();
factoryQ3.createEngine().engine();
factoryQ3.createBrake().brake();
System.out.println("---------------");
}
}
//输出
普通轮胎
国产发动机
普通制动
------------------
//基类,步骤一
public interface Basis{//(public abstract class Basis)
void show();
}
//步骤二:实现基类
public class A implements Basis {
@Override
public void show(){
System.out.println("A重写接口方法...");
}
}
//还有很多其他的实现基类的对象A,B,C
//步骤三:创建抽象一个工厂
/**
* 抽象工厂
*/
public abstract class PrinterFactory {
abstract IPrinter createPrinter();
}
//步骤四:创建一个工厂,并重写工厂的方法(具体工厂类,实现具体产品的创建)
public class APrinterFactory extends PrinterFactory {
@Override
IPrinter APrinter() {
return new A();
}
}
//还有很多其他的实现工厂的对象APrinterFactory,APrinterFactory,APrinterFactory
//步骤五:客户端调用
public class Client {
public static void main(String[] args){
//新增不同的产品,只需要提供不同的创建工厂实例
Aactory aPrinterFactory = new AinterFactory();
//编译看左,运行看右,所以真实运行的是右边也就是工厂的实现类
Basis a = APrinterFactory.APrinter();
//工具工厂的实现调用对应的方法返回产品对象
a.show();
//得到产品对象在调用具体方法
}
}
//以上即是一个完成的工厂方法的写法,和普通的工厂模式相比只是把工厂也抽象了
工厂方法模式: 只有一个抽象产品类,具体工厂类只能创建一个具体产品类的实例
抽象工厂模式: 有多个抽象产品类 ,具体工厂类能创建多个具体产品类的实例(这个更加模块化)
工厂方法模式 | 抽象工厂模式 |
---|---|
针对的是一个产品等级结构 | 针对的是面向多个产品等级结构 |
一个抽象产品类 | 多个抽象产品类 |
可以派生出多个具体产品类 | 每个抽象产品类可以派生出多个具体产品类 |
一个抽象工厂类,可以派生出多个具体工厂类 | 一个抽象工厂类,可以派生出多个具体工厂类 |
每个具体工厂类只能创建一个具体产品类的实例 | 每个具体工厂类只能创建一个具体产品类的实例 |
在换一种说法就是,抽象工厂是工厂方法的加强版,工厂方法可以造汽车(汽车要1,2,3,零件),但是用抽象工厂模式就可以造A,B<C汽车,然后还可以造对于的汽车型号的零件
使用场景
方便地构造对象实例,而不必关心构造对象实例的细节和复杂过程。并且构造的实例过程比较复杂。
2.单例设计模式
https://blog.csdn.net/qq_37904799/article/details/81807954
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造方法的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
总结:
1.构造方法设为private(这样,就不能用new操作符在类的外部产生类的对象了)(构造方法时来实例化这个对象的)
如果设置为私有的就不能从外部实例化这个对象了,只能自己实例化自己
2.调用该类的某个静态方法以返回该类内部创建的对象
概念
java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍二种:懒汉式单例、饿汉式单例。
单例模式有以下特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例设计模式——饿汉式
具体分析单例模式(不创建方法,只是自己创建一个类来实例化自己。,但是如果把singleton变成了null这时候就相当于引用了两个地址,也就不符合单例的定义了,所以进阶版就是体感了公共的访问方法然后返回)
所谓饿汉式,就是直接创建出类的实例化;
public class test {
public static void main(String[] args) {
//分别实例化对象 s1,s2
Singleton s1 = Singleton.singleton;
//这里突然 将Singleton中的变成了null
// Singleton.singleton = null;
Singleton s2 = Singleton.singleton;
System.out.println(s1 == s2);
}
}
class Singleton{
//私有的构造函数,保证外类不能实例化本类
private Singleton(){}
//自己创建一个类的实例化
public static Singleton singleton = new Singleton();
}
进阶版就是提供了公共的访问方法然后返回
package com.hahaha.java;
public class TestSingleton {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
//比较引用值
System.out.println(s1==s2);
}
}
/*
*放在两个类中,因为在同一个包下,所以可以直接引用
*/
//只能创建Singleton的单个实例
class Singleton{
//1.私有化构造器,使得在类的外部不能够调用此构造器
private Singleton(){}
//2.在类的内部创建一个类的实例
private static Singleton instance = new Singleton();
//3.私有化此对象,通过公共的方法来调用
public static Singleton getInstance(){
return instance;
}
}
单例模式——懒汉式
而对于懒汉式,就是在需要的时候再创建类的实例化
package com.hahaha.java;
//关于懒汉式的线程安全问题:使用同步机制
//对于一般的方法内,使用同步代码块,可以考虑使用this
//对于静态方法而言,使用当前类本身充当
public class TestSingleton2 {
public static void main(String[] args) {
Singleton2 s1 = Singleton2.getInstsnce();
Singleton2 s2 = Singleton2.getInstsnce();
System.out.println(s1==s2);
System.out.println();
}
}
class Singleton2{
private Singleton2(){}
private static Singleton2 instance = null;
public static Singleton2 getInstsnce(){
synchronized (Singleton2.class) {
if (instance==null) {
instance = new Singleton2();
}
return instance;
}
}
}
比较两种创建方式:
饿汉式:简单来说就是空间换时间,因为一上来就实例化对象,占用了内存(不管我用不用这个我都创建)
懒汉式:简单来说就是时间换空间,与饿汉式相反
两者谁更好,这里举个例子来说明(此例子验证的是饿汉式比懒汉式更好)
public static Singleton getInstance(){
//判断singleton是否为null,如果为null,即判定需要实例化
if (singleton == null) {
//
singleton = new Singleton();
}
return singleton;
}
比如,现在有A线程和B线程,A线程刚好在这个getInstance()方法中,刚刚判断完非空(此时为null),即需要创建实例,然而,就是这么巧,B线程抢到了CPU的执行权,A线程sleep了,这时,B线程也进行了这个判断,和A一样,都需要创建实例,而这时,A也抢到了CPU,这时,B就sleep了,然后A执行了实例化后,B又抢到CPU执行权,然后B也实例化,这时,出现问题了,A和B都实例化了一个对象,这就是赤果果的两个对象呀,单例呢,唯一呢,全都没了。
简而言之就是线程争抢问题,因为线程获取这个cpu是不可控的,
而且,上面说过,懒汉式和饿汉式的区别具体就是时间和空间的转换,现在开发的时候关心的应该还是时间,对于空间,完全可以通过硬件来优化呀,加大内存!!!但是减少时间计算就很麻烦了,额!!
所以说,对于懒汉式在多线程中式不支持的(仅个人观点,毕竟我的水平还不熟练),所以相对来说,更多的是用饿汉式
之前百度单例的时候,发现还有第三种方法,简单试了一下,也能达到单例的效果,但是没有具体尝试,这里粘个代码和运行结果看看就好了
public class test {
public static void main(String[] args) {
// 分别实例化对象 s1,s2
Singleton s1 = Singleton.singleton;
// 在这里 更改singleton的值是不允许的,因为设置了final属性
// Singleton.singleton = null;
Singleton s2 = Singleton.singleton;
System.out.println(s1 == s2);
}
}
class Singleton {
// 私有的构造函数,保证外类不能实例化本类
private Singleton() {
}
// 自己创建一个类的实例化
public static final Singleton singleton = new Singleton();
}
场景
1.封装一些常用的工具类,保证整个应用常用的数据统一
2.方便资源相互通信的环境,保存一些共享数据在内存中,其他类随时可以读取。
3.需要生成唯一序列的环境
在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例
优点:
1、提供了对唯一实例的受控访问。
2、由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
3、允许可变数目的实例。
缺点:
1、由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
2、单例类的职责过重,在一定程度上违背了“单一职责原则”。
3、滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
3.装饰者模式
装饰者与被装饰者拥有共同的超类,继承的目的是继承类型,而不是行为(类似俄罗斯套娃)
本题例子:https://blog.csdn.net/jason0539/article/details/22713711
咖啡例子:https://www.cnblogs.com/bearduncle/p/8110253.html
//定义被装饰者
public interface Human {
public void wearClothes();
public void walkToWhere();
}
//定义装饰者
public abstract class Decorator implements Human {
private Human human;
public Decorator(Human human) {
this.human = human;
}
public void wearClothes() {
human.wearClothes();
}
public void walkToWhere() {
human.walkToWhere();
}
}
//下面定义三种装饰,这是第一个,第二个第三个功能依次细化,即装饰者的功能越来越多
public class Decorator_zero extends Decorator {
public Decorator_zero(Human human) {
super(human);
}
public void goHome() {
System.out.println("进房子。。");
}
public void findMap() {
System.out.println("书房找找Map。。");
}
@Override
public void wearClothes() {
// TODO Auto-generated method stub
super.wearClothes();
goHome();
}
@Override
public void walkToWhere() {
// TODO Auto-generated method stub
super.walkToWhere();
findMap();
}
}
public class Decorator_first extends Decorator {
public Decorator_first(Human human) {
super(human);
}
public void goClothespress() {
System.out.println("去衣柜找找看。。");
}
public void findPlaceOnMap() {
System.out.println("在Map上找找。。");
}
@Override
public void wearClothes() {
// TODO Auto-generated method stub
super.wearClothes();
goClothespress();
}
@Override
public void walkToWhere() {
// TODO Auto-generated method stub
super.walkToWhere();
findPlaceOnMap();
}
}
public class Decorator_two extends Decorator {
public Decorator_two(Human human) {
super(human);
}
public void findClothes() {
System.out.println("找到一件D&G。。");
}
public void findTheTarget() {
System.out.println("在Map上找到神秘花园和城堡。。");
}
@Override
public void wearClothes() {
// TODO Auto-generated method stub
super.wearClothes();
findClothes();
}
@Override
public void walkToWhere() {
// TODO Auto-generated method stub
super.walkToWhere();
findTheTarget();
}
}
//定义被装饰者,被装饰者初始状态有些自己的装饰
public class Person implements Human {
@Override
public void wearClothes() {
// TODO Auto-generated method stub
System.out.println("穿什么呢。。");
}
@Override
public void walkToWhere() {
// TODO Auto-generated method stub
System.out.println("去哪里呢。。");
}
}
//测试类,看一下你就会发现,跟java的I/O操作有多么相似
public class Test {
public static void main(String[] args) {
Human person = new Person();
Decorator decorator = new Decorator_two(new Decorator_first(new Decorator_zero(person)));
decorator.wearClothes();
decorator.walkToWhere();
//Human person = new Person();
//Decorator_zero decorator_zero=new Decorator_zero(person);
//Decorator_first decorator_first =new Decorator_first(decorator_zero);
//Decorator decorator = new Decorator_two(decorator_first);
//decorator.wearClothes();
//decorator.walkToWhere();
}
}
链式调用,一层层调用,根据构造这个类对象的时候传递的什么值(对象)去调用改对象的方法,一直调用直到被装饰者的实现类(最里面的要传递这个)然后执行这个方法(被装饰类接口定义的方法)然后一层层退出来.
-------------------------------------------------------------------------------------------------
注意:调用的最里面的那个方法时根据装饰者去调用的(传给装饰者的值)(这个传值是在建立对象的时候就传过去了)
然后根据这个值一层层调用
/*输出结果*/
穿什么呢。。
进房子。。
去衣柜找找看。。
找到一件D&G。。
去哪里呢。。
书房找找Map。。
在Map上找找。。
在Map上找到神秘花园和城堡。。
/*输出结果*/
总结:
第一:就是被装饰者是接口类,
第二:被装饰者有一个实现类
第三:装饰者要继承被装饰者(因为要装饰这个被装饰者)(为了可以扩展所以为abstract类即抽象类)
第四:所有的装饰也就是完善被装饰者的类都是继承装饰者(通过装饰者去给被装饰者加上装饰),然后里面要重写(被装饰者的)方法在这个里面调用自己本类的方法(也就是装饰)
第五:装饰可以多层嵌套的加,
(例如对于一个人可以加T恤,在T恤上加毛衣,在毛衣上加外套)
第六:传值的问题(此处用到运行时的多态编译看左,运行看右并且因为要多级调用父类的方法(super所以要传对象,你到底用哪个对象来调用被装饰者的方法(即最底层的方法)))
补充:
java中继承接口就必须实现这个接口的全部方法,除非这是一个抽象类或者也是一个接口
不一定,关键要看子类是否是抽象类。
如果子类是非抽象类,则必须实现接口中的所有方法;
如果子类是抽象类,则可以不实现接口中的所有方法,因为抽象类中允许有抽象方法的存在!
1、抽象类定义
抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。通常在编程语句中用 abstract 修饰的类是抽象类。在C++中,含有纯虚拟函数的类称为抽象类,它不能生成对象;在java中,含有抽象方法的类称为抽象类,同样不能生成对象。抽象类是不完整的,它只能用作基类。在面向对象方法中,抽象类主要用来进行类型隐藏和充当全局变量的角色。
2、抽象类特点
1)抽象类不能实例化。
2)抽象类可以包含抽象方法和抽象访问器。
3)不能用 sealed 修饰符修饰抽象类,因为这两个修饰符的含义是相反的。 采用 sealed 修饰符的类无法继承,而 abstract 修饰符要求对类进行继承。
4)从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实际实现。
3、与具体类的比较
1)抽象类不能直接实例化,并且对抽象类使用 new 运算符会导致编译时错误。虽然一些变量和值在编译时的类型可以是抽象的,但是这样的变量和值必须或者为 null,或者含有对非抽象类的实例的引用(此非抽象类是从抽象类派生的)。
2)允许(但不要求)抽象类包含抽象成员。
3)抽象类不能被密封。
4、与接口的比较
1)相同点
a、不能实例化;
b、包含未实现的方法声明;
c、派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员)
2)不同点
a、类可以实现无限个接口,但仅能从一个抽象(或任何其他类型)类继承,从抽象类派生的类仍可实现接口,从而得出接口是用来解决多重继承问题的。
b、抽象类当中可以存在非抽象的方法,可接口不能,且它里面的方法只是一个声明必须用public来修饰没有具体实现的方法。
c、抽象类中的成员变量可以被不同的修饰符来修饰,可接口中的成员变量默认的都是静态常量(static final)。
d、抽象类是对象的抽象,然而接口是一种行为规范。
4.代理模式
概念
为其他对象提供一种代理以控制对这个对象的访问。在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
CSDN:静态代理
静态代理相比动态代理更加简单,他是在编译期就可以确定。
代理接口的定义:
代理模式中要求代理对象与被代理对象必须实现同一个接口,同时代理中持有一个被代理的引用。设置我们设想通过中介买房子,那么客户就是被代理对象,中介就是代理,行为就是买房,因此可以定义一个买房的接口。
public interface Buy {
void buyHouse(long money);
}
被代理类
被代理类实现定义的接口,并执行相关操作
public class Buyer implements Buy{
@Override
public void buyHouse(long money) {
System.out.println("买房子花了:"+money+"元---");
}
}
代理类
代理类也要实现定义的接口,并执行相关操作
public class ProxyBuyer implements Buy {
//被代理类(客户),代理类持有被代理的对象,即Buyer
private Buy buy;
public ProxyBuyer(Buy buy) {
this.buy = buy;
}
@Override
public void buyHouse(long money) {
buy.buyHouse(money);
System.out.println("中介费:" + money*0.02 + "元");
}
}
测试
public void staticProxy(View view){
System.out.println("----------------静态代理-----------------");
Buy buyer = new Buyer();
ProxyBuyer proxyBuyer = new ProxyBuyer(buyer);
proxyBuyer.buyHouse(500000);
}
/*测试结果*/
----------------静态代理-----------------
买房子花了:500000元---
中介费:10000元
/*测试结果*/
CSND:动态代理
和静态代理相比他可以实现AOP编程,这是静态代理无法实现的,另外还可以解耦,无侵入式的扩展代码。Java中的动态代理要求必须实现InvocationHandler接口,和静态代理一样也必须持有被代理对象。定义一个类ProxyBuyer2实现InvocationHandler,重写invoke方法,在invoke方法中有三个参数:
invoke(Object proxy, Method method, Object[] args)
-
参数一:代理类的实例,无实际作用。
-
参数二:要调用的方法。
-
参数三:方法的参数。
public class ProxyBuyer2 implements InvocationHandler { //动态代理中持有被代理对象 private Buy buy; public ProxyBuyer2(Buy buy) { this.buy = buy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("buyHouse")){ //如果是买房子的方法的话,取出第一个参数money,这个money为接口中定义的参数,为已知数据 long money = (long) args[0]; System.out.print("中介费:"+money*0.02); return method.invoke(buy,args); } return null; } }
测试
public void motionProxy(View view){ System.out.println("----------------d动态代理-----------------"); Buy buyer = new Buyer(); ProxyBuyer2 proxyBuyer2 = new ProxyBuyer2(buyer); Buy proxy = (Buy) Proxy.newProxyInstance(buyer.getClass().getClassLoader(),buyer.getClass().getInterfaces(),proxyBuyer2); proxy.buyHouse(500000); } /*测试结果*/ ----------------动态代理----------------- 中介费:10000元 买房子花了:500000元--- /*测试结果*/
newProxyInstance方法接收三个参数:
- 参数一:被代理类的类加载器ClassLoader。
- 参数二:被代理类实现的自定义的接口。
- 参数三:被代理对象。
通过源码分析得知newProxyInstance主要做了三件事情:
1、校验传入参数的正确性。
2、根据传入的参数生成一个新类的calss对象。
Class<?> cl = getProxyClass0(loader, intfs);
3、根据生成的class通过反射获取构造函数对象并生成代理类实例,实现定义的接public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //1、检查h不为空,否则抛异常 Objects.requireNonNull(h); //2、对传入的接口做安全检查 final Class<?>[] intfs = interfaces.clone(); //3、根据传入参数生成ProxyClass0对象class对象 Class<?> cl = getProxyClass0(loader, intfs); //4、根据class使用反射生成代理对象的实例 try { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { cons.setAccessible(true); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { --------------------------省略非核心的异常处理逻辑代理------------------------------- } }
总结
1、代理与被代理实现同一个接口,即拥有共同的行为,同时代理中持有一个被代理的引用。
2、代理的主要作用是方法增强,解耦,并且物侵入式的的扩展方法。
3、静态代理需要我们编码时就确定代理、被代理对象关系;动态代理通过反射的方式自动生成代理对象。
4、静态代理只能针对特定一种产品,而动态代理则可以对所有类型产品,因为他的代理类是动态生成。当代理对象比较多的时候需要写大量的代理类,因此动态代理更为常用。
简书:静态代理
代理设计模式,代理类和真实类有相同接口,代理类对外隐藏真实类,使用者只关系使用的接口功能,不清楚使用的是代理类还是真实类,满足功能即可。
静态代理,编译时,将接口,代理类,真实类定义好,class文件生成,按照Jvm正常的加载流程运行。
举例,接口。
public interface Base {
String hi();
}
代理类
public class ProxyBase implements Base{
RealBase real ;
public ProxyBase(RealBase real) {
super();
this.real = real;
}
@Override
public String hi() {
return real.hi();
}
}
真实类
public class RealBase implements Base {
@Override
public String hi() {
return "Real";
}
}
真实类和代理类都实现Base接口,代理类引用真实类对象,代理类方法调用真实类方法。
动态代理
动态代理,运行时,根据接口动态生成一个新代理类,优点是可以对所有的代理类实现的接口方法统一处理,路由到一个地方,InvocationHandler的invoke方法,通过Method识别调用的代理方法,触发真实类target的对应方法,也可以对所有方法统一加内容,比如统计打点时间。
举例,Retrofit类,利用动态代理生成interface的代理对象。
public <T> T create(final Class<T> service) {
...
return (T) Proxy.newProxyInstance(service.getClassLoader(),
new Class<?>[] { service }, new InvocationHandler() {
//该对象传入$Proxy0的构造方法中
private final Platform platform = Platform.get();
@Override
public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
...
}
});
}
Retrofit类的create方法,调用Proxy类的newProxyInstance方法,参数是类加载器ClassLoader,interface数组,InvocationHandler。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler invocationHandler)
throws IllegalArgumentException {
Exception cause;
try {
return getProxyClass(loader, interfaces)
.getConstructor(InvocationHandler.class)
.newInstance(invocationHandler);
} catch (NoSuchMethodException e) {
}
}
Class<?>[]数组只有一个interface元素,Xxx.class服务类型。
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException {
final List<Class<?>> interfaceList =
new ArrayList<Class<?>>(interfaces.length);
...
synchronized (loader.proxyCache) {
//缓存中查找
Class<?> proxy = loader.proxyCache.get(interfaceList);
if (proxy != null) {
return proxy;
}
}
...
List<Method> methods = getMethods(interfaces);
Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
...
String baseName = commonPackageName != null && !commonPackageName.isEmpty()
? commonPackageName + ".$Proxy"
: "$Proxy";
Class<?> result;
synchronized (loader.proxyCache) {
result = loader.proxyCache.get(interfaceList);
if (result == null) {
//Proxy每生成一个$Proxy,nextClassNameIndex(静态)自动+1
String name = baseName + nextClassNameIndex++;
result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray);
loader.proxyCache.put(interfaceList, result);
}
}
return result
}
保存一份Map缓存,List<Class<?>>是key,value是interface服务的代理类Class<?> proxy。
遍历Class<?>数组,获取List,排序,增加equal,toString,hashCode三个基类方法。
根据name,Class<?>数组和List,native方法,即generateProxy方法,创建一个新类,命名为Xxx$Proxy数字,新类继承Proxy类,实现interface的方法。
generateProxy方法,创建Class<?>类型。
Map缓存防止每次对相同interface服务创建动态代理时都生成一次新类,反射影响性能。
getConstructor方法,获取新类带InvocationHandler参数的构造方法,创建新类实例对象。
protected Proxy(InvocationHandler h) {
this.h = h;
}
将新类的对象返回,**对Proxy0类反编译,该类中所有的interface方法会触发super.h.invoke方法。**父类的h是创建新类对象时传入的InvocationHandler对象。因此,每一个interface方法调用,被InvocationHandler的invoke方法拦截。
总结
动态代理这一套固定代码可以无侵入扩展各种服务interface的api。核心逻辑是创建一个实现interface接口方法的Proxy代理子类,实现的interface方法均由我们上层强行插入的InvocationHandler统一路由处理。
利用Proxy#方法,根据虚拟机运行时动态生成新类,创建一个对象,命名方式以$开头,Proxy+数字,继承Proxy。
新创建类和interface的关系加入Map缓存,防止每次创建同一个服务的动态代理类时都生成一次新类,影响性能。
使用场景
1、Windows 里面的快捷方式。 2、猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。 3、买火车票不一定在火车站买,也可以去代售点。 4、一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。 5、spring aop。
说到代理模式 最经典的就是Spring AOP 就是对目标类和方法进行切面增强的功能,通过在目标类的基础上增加切面逻辑,完成一些和程序业务无关的内容。主要有两种实现模式: 一种是JDK的动态代理,一种是Cglib代理
这两种的区别是:
JDK动态代理只能代理实现了接口的目标类
Cglib则不需要 Cglib是基于类的代理 原理是对目标类生成一个子类然后覆盖和实现它的所有方法并增强 所有Cglib不能对final修饰的类进行代理。
这里提供一个JDK代理的实例
package com.Model.Proxy;
//操作定义
public interface SubjectOperations {
// 打印操作
public void print();
// 打印输入字符串操作
public void printfStr(String string);
}
package com.Model.Proxy;
public class RealSubject implements SubjectOperations {
@Override
public void print() {
System.out.println("我实现了接口 完成这个打印操作");
}
@Override
public void printfStr(String string) {
// TODO Auto-generated method stub
System.out.println("打印输入的内容: " + string);
}
}
package com.Model.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class LogHandler implements InvocationHandler {
private Object ImpClass;
public LogHandler(Object realObject) {
this.ImpClass = realObject;
}
public Object bind(Object impclass) {
this.ImpClass = impclass;
return Proxy.newProxyInstance(impclass.getClass().getClassLoader(), impclass.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("在调用代理的对象的真实方法前 我先调用一些自己的方法和规则..... 下面调用真实方法");
System.out.println("Method: " + method);
method.invoke(ImpClass, args);
System.out.println("调用代理的对象的真实方法后, 我进行一些逻辑处理 上面是调用的真实方法");
return null;
}
public static void main(String[] args) {
}
}
class Client {
public static void main(String[] args) {
RealSubject subject = new RealSubject();
LogHandler handler = new LogHandler(subject);
// 转化成接口 只能代理实现了接口的类
SubjectOperations pSubject1 = (SubjectOperations) handler.bind(subject);
System.out.println(pSubject1.getClass().getName());
pSubject1.print();
pSubject1.printfStr("YYYYY");
}
}
控制台输出:
com.sun.proxy.$Proxy0
在调用代理的对象的真实方法前 我先调用一些自己的方法和规则..... 下面调用真实方法
Method: public abstract void com.Model.Proxy.SubjectOperations.print()
我实现了接口 完成这个打印操作
调用代理的对象的真实方法后, 我进行一些逻辑处理 上面是调用的真实方法
在调用代理的对象的真实方法前 我先调用一些自己的方法和规则..... 下面调用真实方法
Method: public abstract void com.Model.Proxy.SubjectOperations.printfStr(java.lang.String)
打印输入的内容: YYYYY
调用代理的对象的真实方法后, 我进行一些逻辑处理 上面是调用的真实方法
设计模式总述
分类
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
结构型模式,共七种:适配器模式、装饰者模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式。
————————————————
版权声明:本文为CSDN博主「炸斯特」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jason0539/article/details/44956775
原则
总原则:开放封闭原则:对扩展开放,对修改封闭,意即程序拓展时不要动原有的代码
1.单一职责原则:不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,否则就应该把类拆分。
2.LSP原则(里氏替换原则):任何基类可以出现的地方,子类一定可以出现
任何基类可以出现的地方,子类一定可以出现。里氏替换原则是继承复用的基石,只有当衍生类可以替换基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
里氏代换原则是对“开-闭”原则的补充。实现“开闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。里氏替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。
3.依赖倒置原则:使用接口,依赖于抽象而不是具体
面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互
4.接口隔离原则:为了解耦,使用多个相互隔离的接口
每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。
5.迪米特法则:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
一个类对自己依赖的类知道的越少越好。无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。
最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。
6.CRP法则(合成复用原则):尽量使用合成/聚合的方式,而不是使用继承。
本文地址:https://blog.csdn.net/qq_43180019/article/details/110814429
上一篇: Docker 镜像、容器、仓库
下一篇: 文件导出的get和post方式