设计模式(适用场景 优点)
单例模式
确保单例类只有一个实例,并且这个单例类提供一个函数接口让其他类获取到这个唯一的实例。
什么时候需要使用单例模式呢:如果某个类,创建时需要消耗很多资源,即new出这个类的代价很大;或者是这个类占用很多内存,如果创建太多这个类实例会导致内存占用太多。
//此为懒汉式 饿汉式在声明时直接初始化
public class Singleton{
//volatile保证了其他线程拿到的也是最新的实例
private volatile static Singleton instance;
//将默认的构造函数私有化,防止其他类手动new
private Singleton(){};
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
为什么需要2次判断是否为空呢?第一次判断是为了避免不必要的同步,第二次判断是确保在此之前没有其他线程进入到sychronized块创建了新实例。
使用类级内部类,只有内部类被调用到(即Singleton.singleton)才会装载
public class MySingleton {
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
* 没有绑定的关系,而且只有被调用到才会装载,从而实现了延迟加载
*/
private static class Singleton{
/**
* 静态初始化器,用JVM来保证线程安全
*/
private static MySingleton singleton = new MySingleton();
static {
System.out.println("---->类级的内部类被加载");
}
private Singleton(){
System.out.println("---->类级的内部类构造函数被调用");
}
}
//私有化构造函数
private MySingleton(){
System.out.println("-->开始调用构造函数");
}
public static MySingleton getInstance(){
System.out.println("-->开始调用公有方法返回实例");
System.out.println("-->返回单例");
return Singleton.singleton;
}
}
记忆:getSystemService的内部实现
建造者模式(Builder)
将一个复杂对象的构造与它的表示分离,使得同样的构造过程可以创建不同的表示。
主要是在创建某个对象时,需要设定很多的参数(通过setter方法),但是这些参数必须按照某个顺序设定,或者是设置步骤不同会得到不同结果(模式不同后续的可用属性也有不同)
记忆:在创建对话框时的代码
身边例子:通过方法构建一个页面,有无list,有无tab,有无column,每一个先导选择都会导致后续使用参数的不同
工厂方法模式
定义一个创建对象的接口,让子类决定实例化哪个类
其实,在getSystemService方法中就是用到了工厂模式,他就是根据传入的参数决定创建哪个对象,当然了,由于返回的都是以单例模式存在的对象,因此不用new了,直接把单例返回就好。
抽象工厂模式
为创建一组相关或者是相互依赖的对象提供一个接口,而不需要制定他们的具体类 (满足依赖倒置原则)
public abstract class AbstractProductA{//标题栏
public abstract void method();
}
public abstract class AbstractProdectB{//无数据
public abstract void method();
}
public abstract class AbstractFactory{//listTemplate
public abstract AbstractProductA createProductA();
public abstract AbstractProductB createProductB();
}
public class ConcreteFactory1 extends AbstractFactory{//listT实例1
public AbstractProductA createProductA(){
return new ConcreteProductA1();
}
public AbstractProductB createProductB(){
return new ConcreteProductB1();
}
}
public class ConcreteFactory2 extends AbstractFactory{//listT实例2
public AbstractProductA createProductA(){
return new ConcreteProductA2();
}
public AbstractProductB createProductB(){
return new ConcreteProductB2();
}
}
策略模式
定义:有一系列的算法,将每个算法封装起来(每个算法可以封装到不同的类中),各个算法之间可以替换,策略模式让算法独立于使用它的客户而独立变化。
其中在属性动画中使用时间插值器的时候就用到了。在使用动画时,你可以选择线性插值器、加速减速插值器、减速插值器以及自定义的插值器。这些插值器都是实现根据时间流逝的百分比来计算出当前属性值改变的百分比。通过根据需要选择不同的插值器,实现不同的动画效果。
使用者可能会觉得有工厂方法的影子,因为上文中提到的线性插值器、加速减速插值器、减速插值器以及自定义的插值器,很明显可以通过工厂方法构建出来,但是策略模式是代表了将每个算法封装起来(每个算法可以封装到不同的类中),各个算法之间可以替换,策略模式让算法独立于使用它的客户而独立变化,侧重点不同。所以我们要知道,同时使用多种设计模式是很常见的。
身边的例子:生成申请类的工厂方法,只需要一个变量的改变,如果申请类内部存在算法,则应用了策略模式。可以说工厂方法可以作为策略模式的实现方式,但他不是唯一的
反例:使用了建造者模式的工厂方法
适配器模式
定义:把一个类的接口变换成客户端所期待的另一个接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
比较典型的有ListView和RecyclerView的adapter。为什么ListView需要使用适配器呢?主要是,ListView(view)只关心它的每个ItemView,而不关心这个ItemView具体显示的是什么。而我们的数据源(model)存放的是要显示的内容,它保存了每一个ItemView要显示的内容。ListView和数据源之间没有任何关系,这时候,需要通过适配器(adapter),适配器提供getView方法给ListView使用,每次ListView只需提供位置信息给getView函数,然后getView函数根据位置信息向数据源获取对应的数据,根据数据返回不同的View。
观察者模式
定义:定义了对象之间的一对多的关系,其实就是1对n,当“1”发生变化时,“n”全部得到通知,并更新。
观察者模式一个比较经典的应用就是:订阅——发布系统。很容易理解,发布消息时,将消息发送给每个订阅者。
那么Android哪里用到了观察者模式呢?我们看看ListView的适配器,有个函数notifyDataSetChanged()函数,这个函数其实就是通知ListView的每个Item,数据源发生了变化,请各位Item重新刷新一下。
//抽象主题
public interface MySubject {
void registerObserver(MyObserver o);
void removeObserver(MyObserver o);
void notifyObserver();
}
//抽象观察者
public interface MyObserver {
void update(String authorName, String articleName);
}
//实现主题
public class WeChatServer implements MySubject {
private List<MyObserver> myObservers;
private String authorName;
private String articleName;
public WeChatServer(String authorName) {
myObservers = new ArrayList<>();
this.authorName = authorName;
}
public void publishArticle(String articleName) {
this.articleName = articleName;
notifyObserver();
}
@Override
public void registerObserver(MyObserver o) {
myObservers.add(o);
}
@Override
public void removeObserver(MyObserver o) {
if (myObservers.contains(o)) {
myObservers.remove(o);
}
}
@Override
public void notifyObserver() {
myObservers.forEach(item -> {
item.update(authorName, articleName);
});
}
}
//具体的观察者
public class WeChatClient implements MyObserver {
private String username;
public WeChatClient(String username) {
this.username = username;
}
@Override
public void update(String authorName, String articleName) {
System.out.println(username + ": " + authorName + " 发了一篇文章 " + articleName);
}
}
public class Main {
public static void main(String[] args) {
WeChatServer weChatServer = new WeChatServer("Java识堂");
WeChatClient user1 = new WeChatClient("张三");
WeChatClient user2 = new WeChatClient("李四");
weChatServer.registerObserver(user1);
weChatServer.registerObserver(user2);
weChatServer.publishArticle("《五分钟学会观察者模式》");
}
}
装饰模式
动态的给一个对象添加额外的职责,就增加功能来说,装饰模式比子类继承的方式更灵活。
通过简单代码来理解装饰模式:
public abstract class Component{
public abstract void operate();
}
public class ConcreteComponent extends Component{
public void operate(){
//具体的实现
}
}
public class Decorator{
private Component component;
public Decorator(Component component){
this.component=component;
}
public void operate(){
operateA();
component.operate();
operateB();
}
public void operateA(){
//具体操作
}
public void operateB(){
//具体操作
}
}
那么在Android哪里出现了装饰模式呢?我们平时经常用到Context类,但是其实Context类只是个抽象类,具体实现是ContextImpl,那么谁是ContextImpl的装饰类呢?我们知道Activity是个Context,但是Activity 并不是继承于Context,而是继承于ContextThremeWrapper.而ContextThremeWrapper继承于ContextWrapper,ContextWrapper继承Context.说了这么多,跟装饰模式有啥关系?主要是引入ContextWrapper这个类。ContextWrapper内部有个Context引用mContext,并且ContextWrapper中对Context的每个方法都有实现,在实现中调用的就是mContext相同的方法。
身边的例子:BaseActivity+MyBaseActivity(通过抽象类+实现类的组合实现职责添加)