移动架构01-设计模式
设计模式是一套设计标准,用来指导实际的设计工作。作用是提高工作效率。
Android常见的设计模式:工厂模式、建造者模式、单例模式、观察者模式、代理模式、AOP。
一、工厂模式
工厂,顾名思义就是批量生成产品的车间。
工厂模式是指根据要求生成相同或相似的对象的设计模式。作用是统一管理相同接口的对象的实例化。具体来说,一个接口通常有很多实现类,如果创建这些实现类需要先做一些逻辑判断或初始化操作,这个时候就可以用工厂模式来统一创建。
1.工厂模式的分类
关于工厂模式的分类,有两种说法:
- 分成简单工厂模式和抽象工厂模式,比如:百度百科;
- 分成简单工厂模式、工厂方法模式和抽象工厂模式,比如:各大博客网站;
个人比较倾向与第一张说法,理由有二:
- 工厂方法模式中工厂类使用的是抽象类,却一定要叫工厂方法模式,太过牵强;
- 工厂方法模式中只能生成一种对象,可是连简单工厂模式都能生产多种对象,工厂方法模式却不能,不太合理。
下面,按照第一种说法来介绍工厂模式!
2.简单工厂模式
简单工厂模式就是只有一个工厂类的工厂模式。
//工厂类
public class Factory {
public static Api create(int type){
switch (type){
case 1:
return new ImplA();
case 2:
return new ImplB();
default:
return new ImplA();
}
}
}
//产品接口
public interface Api {
void operator();
}
//具体的产品A
public class ImplA implements Api {
@Override
public void operator() {
System.out.println("完成了一种操作A");
}
}
//具体的产品B
public class ImplB implements Api {
@Override
public void operator() {
System.out.println("完成了一种操作B");
}
}
public class Test {
public static void main(String[] args) {
//通过简单工厂模式创造ImplB对象
Api api = Factory.create(2);
api.operator();
}
}
3.抽象工厂模式
抽象工厂模式就是工厂类抽象化的工厂模式。
为什么要使用抽象工厂模式呢?当出现新的产品时,如果使用简单工厂模式,就需要修改原先的工厂类,这样就违反了Java的开闭原则。这个时候,就使用抽象工厂模式,创意一个新的工厂类来生产新的产品。
//工厂类接口(抽象的工厂类)
public interface IFactory {
Api create(int type);
}
//新的工厂类
public class Factory2 implements IFactory{
public Api create(int type){
switch (type){
case 1:
return new ImplA();
case 2:
return new ImplB();
case 3:
return new ImplC();
default:
return new ImplC();
}
}
}
public class Test {
public static void main(String[] args) {
//通过抽象工厂模式创造ImplC对象
Api api = new Factory2().create(3);
api.operator();
}
}
二、建造者模式
建造者模式是用来生产复杂对象的设计模式。作用是将一个复杂对象的创建过程转化为多个简单的创建过程。
//建造者:先分步设置临时对象的属性,然后创建实际的对象,并统一设置属性。
public class WorkBuilder {
private RoomParams params;
public WorkBuilder() {
this.params = new RoomParams();
}
public WorkBuilder makeWindow(String window) {
params.window = window;
return this;
}
public WorkBuilder makFloor(String floor) {
params.floor = floor;
return this;
}
public Room build() {
Room room = new Room();
room.apply(params);
return room;
}
// 用来设置Room的属性
class RoomParams {
public String window;
public String floor;
}
}
//需要创建的复杂对象
public class Room {
private String window;
private String floor;
public void apply(WorkBuilder.RoomParams parmas) {
window = parmas.window;
floor = parmas.floor;
}
@Override
public String toString() {
return "Room{" + "window='" + window + '\'' + ", floor='" + floor + '\'' + '}';
}
}
public class Test {
public void test(){
//使用建造者模式创建复杂对象
Room room=new WorkBuilder().makeWindow("法式").makFloor("英式").build();
System.out.println(room);
}
}
三、单例模式
单例模式是内存中只有一个实例的设计模式。作用有2点:
- 减小频繁创建与销毁对象带来的开销;
- 保证核心对象的唯一性。
单例模式的分类:饿汉式、懒汉式、双重检查式、静态内部类。
另外,可以通过Enum(枚举)来实现单例模式,不过Android一般不这么用。
1.饿汉式
饿汉式是在类加载的时候就创建对象。使用懒汉式创建对象时,无法传递参数。
public class SingletonHungry {
//持有私有静态实例,防止被引用,此处直接赋值
private static SingletonHungry instance = new SingletonHungry();
// 私有构造方法,防止被实例化
private SingletonHungry() {
}
// 静态工程方法,创建实例
public static SingletonHungry getInstance() {
return instance;
}
}
2.懒汉式
懒汉式是在获取对象时创建对象。如果一个单例对象从来都不使用,这个时候使用饿汉式(类加载的时候就会创造对象)就会浪费内存,就需要使用懒汉式。
public class SingletonLazy {
//持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载
private static SingletonLazy instance = null;
// 私有构造方法,防止被实例化
private SingletonLazy() {
}
// 静态工程方法,创建实例
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
3.双重检查式
在多线程使用懒汉式,会出现安全问题。如果直接对getInstance方法加synchronized关键字,那么每次调用getInstance()时都会降低性能,所以需要使用双重检查式。
双重检查式是在获取对象时,先判空,然后使用synchronized代码块来创建对象。synchronized代码块内部也需要判空,因为可能在外部判空之后,对象刚好创建好了。
/**
* 双重检查式
*/
public class SingletonLazy2 {
// 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载
private static SingletonLazy2 instance = null;
// 私有构造方法,防止被实例化
private SingletonLazy2() {
}
// 静态工程方法,创建实例
public static SingletonLazy2 getInstance() {
if (instance == null) {
synchronized (instance) {
if (instance == null) {
instance = new SingletonLazy2();
}
}
}
return instance;
}
}
使用双重检查式也存在隐患,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是由于JVM的指令重排机制,JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:
- A、B线程同时进入了第一个if判断
- A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
- 由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
- B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
- 此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。
为了解决上面的问题,需要使用volatile关键字,来保证对象的可见性和有序性(禁止该对象的指令重排)。
private static volatile SingletonLazy2 instance = null;
4.静态内部类
使用volatile关键字的双重检查式存在一个问题:性能差。
所以使用静态内部类来实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。这样我们暂时总结一个完美的单例模式:
public class Singleton {
/* 私有构造方法,防止被实例化 */
private Singleton() {
}
/* 此处使用一个内部类来维护单例 */
private static class SingletonFactory {
private static Singleton instance = new Singleton();
}
/* 获取实例 */
public static Singleton getInstance() {
return SingletonFactory.instance;
}
}
四、观察者模式
观察者模式是一种订阅与通知的设计模式。作用是当对象变化时,统一通知其依赖对象。
观察者模式,是一种被动的观察,类似于邮件订阅。观察者先订阅被观察者,当被观察者发生变化,就会通知观察者。
//观察者接口
public interface IObserver {
public void update();
}
//观察者1
public class Observer1 implements IObserver {
public void update() {
System.out.println("this is Observer1");
}
}
//观察者接口2
public class Observer2 implements IObserver {
public void update() {
System.out.println("this is Observer2");
}
}
//被观察者接口
public interface ISubject {
/*增加观察者*/
public void add(IObserver observer);
/*删除观察者*/
public void del(IObserver observer);
/*通知所有的观察者*/
public void notifyObservers();
}
//被观察者
public class Subject implements ISubject {
private Vector<IObserver> vector = new Vector<IObserver>();
@Override
public void add(IObserver observer) {
vector.add(observer);
}
@Override
public void del(IObserver observer) {
vector.remove(observer);
}
@Override
public void notifyObservers() {
Enumeration<IObserver> enumo = vector.elements();
while(enumo.hasMoreElements()){
enumo.nextElement().update();
}
}
}
public class Test {
public static void main(String[] args) {
ISubject sub = new Subject();
//观察者订阅被观察者
sub.add(new Observer1());
sub.add(new Observer2());
//被观察者通知观察者
sub.notifyObservers();
}
}
五、代理模式
代理模式是通过代理对象对原对象进行扩展的设计模式。优点是不用直接修改原对象。
代理模式分为两种:静态代理和动态代理。
1.静态代理
静态代理是代理对象和被代理对象要实现统一接口的代理模式。
//代理接口
public interface IUserDao {
void save();
}
//被代理对象
public class UserDao implements IUserDao {
@Override
public void save() {
System.out.println("----已经保存数据!----");
}
}
//代理对象
public class UserDaoProxy implements IUserDao {
// 接收保存目标对象
private IUserDao target;
public UserDaoProxy(IUserDao target) {
this.target = target;
}
public void save() {
System.out.println("开始事务...");
target.save();// 执行目标对象的方法
System.out.println("提交事务...");
}
}
public class Test {
public static void main(String[] args) {
// 被代理对象
UserDao target = new UserDao();
// 代理对象,把被代理对象传给代理对象,建立代理关系
UserDaoProxy proxy = new UserDaoProxy(target);
// 执行的是代理的方法
proxy.save();
}
}
2.动态代理
动态代理是是通过反射实现对原对象扩展的代理模式。
// 创建动态代理对象 动态代理不需要实现接口,但是需要指定接口类型
public class ProxyFactory {
// 维护一个被代理对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
// 给被代理对象生成代理对象
// Proxy.newProxyInstance()有3个参数,1是ClassLoader,用来创建对象;2是被代理对象的所有实现接口,用来给代理对象实现;3是InvocationHandler,用来对被代理的方法进行扩展
public Object getProxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务2");
// 执行被代理对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务2");
return returnValue;
}
});
}
}
public class Test {
public static void main(String[] args) {
// 被代理对象
IUserDao target = new UserDao();
// 给被代理对象创建代理对象
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
// 执行方法 【代理对象】
proxy.save();
}
}
六、AOP
AOP(面向切面编程)是一种对多个类统一扩展的设计模式。AOP通过预编译方式和运行期动态代理实现程序功能的统一维护,优点是扩展性比代理模式更强,也更加灵活。
AOP使用aspectjrt框架来实现。
实现步骤
-
引入aspectjrt框架,将aspectjrt.jar添加到libs目录,并在gradle中添加引用:
implementation files('libs/aspectjrt.jar')
; -
配置gradle,使用aspectjrt作为类编译器,从而在编译修改class文件,实现动态扩展;
buildscript { repositories { mavenCentral() } dependencies { classpath 'org.aspectj:aspectjtools:1.8.8' classpath 'org.aspectj:aspectjweaver:1.8.8' } } import org.aspectj.bridge.IMessage import org.aspectj.bridge.MessageHandler import org.aspectj.tools.ajc.Main final def log = project.logger final def variants = project.android.applicationVariants variants.all { variant -> if (!variant.buildType.isDebuggable()) { log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.") return; } JavaCompile javaCompile = variant.javaCompile javaCompile.doLast { String[] args = ["-showWeaveInfo", "-1.8", "-inpath", javaCompile.destinationDir.toString(), "-aspectpath", javaCompile.classpath.asPath, "-d", javaCompile.destinationDir.toString(), "-classpath", javaCompile.classpath.asPath, "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)] log.debug "ajc args: " + Arrays.toString(args) MessageHandler handler = new MessageHandler(true); new Main().run(args, handler); for (IMessage message : handler.getMessages(null, true)) { switch (message.getKind()) { case IMessage.ABORT: case IMessage.ERROR: case IMessage.FAIL: log.error message.message, message.thrown break; case IMessage.WARNING: log.warn message.message, message.thrown break; case IMessage.INFO: log.info message.message, message.thrown break; case IMessage.DEBUG: log.debug message.message, message.thrown break; } } } }
-
自定义注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface BehaviorTrace { String value(); }
-
配置切面
/** * 使用Aspect注解来定义切面, */ @Aspect public class BehaviorTraceAspect { /** * 使用Pointcut注解来定义切入点,指定需要进行切面处理的方法 * Pointcut的参数必须使用指定格式execution(@注释名 注释所在的类 注释所在的方法).*代表所有 */ @Pointcut("execution(@gsw.aop.annotation.BehaviorTrace * *(..))") public void methodAnnotatedWithBehaviorTrace() { } /** * 对切入点进行处理,可以使用三种方式注解(参数为上面使用Pointcut注解的方法): * Before注解:自动在切入点之前运行,不能调用ProceedingJoinPoint.process(); * After注解: 自动在切入点之后运行,不能调用ProceedingJoinPoint.process(); * Around注解:必须调用ProceedingJoinPoint.process()来执行切入点的方法,process()之前的代码在切入点之前执行,process()之后的代码在切入点之后执行; * * @param joinPoint 切入点 */ @Around("methodAnnotatedWithBehaviorTrace()") public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); String className = methodSignature.getDeclaringType().getSimpleName(); String methodName = methodSignature.getName(); String funName = methodSignature.getMethod().getAnnotation(BehaviorTrace.class).value(); //统计时间 long begin = System.currentTimeMillis(); //执行切入点的方法 Object result = joinPoint.proceed(); long duration = System.currentTimeMillis() - begin; Log.d("jett", String.format("功能:%s,%s类的%s方法执行了,用时%d ms", funName, className, methodName, duration)); return result; } }
-
给需要扩展的方法添加自定义注解,然后编译运行即可。
/** * 添加自定义注解,aspect框架会自动进行切入处理。可以添加多个注解。 */ @BehaviorTrace("摇一摇") public void mShake(View view) { SystemClock.sleep(new Random().nextInt(2000)); }
运行结果:功能:摇一摇,ActivityAOP类的mShake方法执行了,用时647 ms
最后
代码地址:https://gitee.com/yanhuo2008/Common/tree/master/app/src/main/java/gsw/common/aop
移动架构专题:https://www.jianshu.com/nb/25128604
喜欢请点赞,谢谢!
网友评论