介绍几种常用的设计模式
单例模式
单例模式,它的定义就是确保某一个类只有一个实例,并且提供一个全局访问点。
应用场景:
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类。
Windows的Task Manager(任务管理器)、Recycle Bin(回收站)、网站访问量计数器
单例模式应用的场景一般发现在以下条件:
1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
2)控制资源的情况下,方便资源之间的互相通信。如线程池等。
package com.lhw.designpattern;
/** * @author lin.hongwen * @date 2020-05-08 */ public class Singleton { private static Singleton singleton; private Singleton() {} public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { singleton = new Singleton(); } } return singleton; } }
工厂模式
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,也就是说工厂方法模式让实例化推迟到子类。
应用场景:
(1)工厂模式是为了解耦:可以将对象的创建和使用分离,如果不分离,不但违反了设计模式的开闭原则,需要需要使用另一个子类的话,需要修改源代码 ,把对象的创建和使用的过程分开。就是Class A 想调用 Class B ,那么A只是调用B的方法,而至于B的实例化,就交给工厂类。
(2)工厂模式可以降低代码重复。
(3)因为工厂管理了对象的创建逻辑,使用者并不需要知道具体的创建过程,只管使用即可,减少了使用者因为创建逻辑导致的错误。
(4)可以通过参数设置,返回不同的构造函数,不需要修改使用类的地方。如果一个类有多个构造方法(构造的重写),我们也可以将它抽出来,放到工厂中,一个构造方法对应一个工厂方法并命名一个友好的名字,这样我们就不再只是根据参数的不同来判断,而是可以根据工厂的方法名来直观判断将要创建的对象的特点。这对于使用者来说,体验比较好。
package com.lhw.designpattern; /** * @author lin.hongwen * @date 2020-05-28 */ public class MyFactory { /** * 大米 */ public static final int TYPE_MI = 1; /** * 油 */ public static final int TYPE_YU = 2; /** * 蔬菜 */ public static final int TYPE_SC = 3; public static FoodBase getFoods(int foodType) { switch (foodType) { case TYPE_MI: return new DaMi(); case TYPE_YU: return new You(); case TYPE_SC: default: return new ShuCai(); } } /** * 测试方法 * @param args */ public static void main (String[] args) { FoodBase foodBase = MyFactory.getFoods(1); foodBase.printName(); foodBase.printNum(); } } abstract class FoodBase { public void printName() { System.out.println("我是一个粮油站"); } public void printNum() { System.out.println("粮油站清点数量"); } } class DaMi extends FoodBase { int num=100; @Override public void printName() { System.out.println("我是一个大米"); } @Override public void printNum(){ System.out.println("大米一共有:" + num); } } class You extends FoodBase { @Override public void printName() { System.out.println("我是一个油"); } } class ShuCai extends FoodBase { @Override public void printName() { System.out.println("我是一个蔬菜"); } }
适配器模式:
所谓适配器模式就是将一个类的接口,转换成客户期望的另一个接口。它可以让原本两个不兼容的接口能够无缝完成对接。
什么时候需要用适配器模式?只要记住一点。当你有动机修改一个已经投入生产的接口,这时候就可以考虑试用适配器模式。适配器模式是用于解决接口不兼容问题有效方法。
package com.lhw.designpattern; /** * 适配器模式 * @author lin.hongwen * @date 2020-05-28 */ public class MyAdapterNew { private MyAdapterOld adapterOld; public MyAdapterNew(MyAdapterOld adapterOld) { this.adapterOld = adapterOld; } /** * 这个方法重写了,也被调用 */ public void doSomething() { adapterOld.doSomething(); System.out.println("new-doSomething"); } /** * 这个方法重写了,但是没用被调用 */ public void howToDo() { adapterOld.howToDo(); } /** * 测试类 * @param args */ public static void main(String args[]) { new MyAdapterNew(new MyAdapterOld()).doSomething(); } } class MyAdapterOld { public void doSomething() { System.out.println("old-doSomething"); } public void howToDo() { System.out.println("old-howToDo"); } /** * 这个方法没有被重写 */ public void nothingToDo() { System.out.println("old-nothingToDo"); } }
装饰模式:
装饰者模式,动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案。虽然装饰者模式能够动态将责任附加到对象上,但是他会产生许多的细小对象,增加了系统的复杂度。
软件设计时必须要考虑到系统的扩展性,但是不可能一次性将所有功能做到满足所有需求。装饰模式的应用,为系统的扩展以及迭代需求提供很好的支持。
package com.lhw.designpattern; /** * @author lin.hongwen * @date 2020-05-29 */ public class MyDecorator { public static void main(String[] args) { Coffee mCoffee = new SimpleCoffee(); mCoffee = new SugarDecorator(mCoffee); mCoffee = new MilkDecorator(mCoffee); int price = mCoffee.getPrice(); String name = mCoffee.getName(); System.out.println("name="+name); System.out.println("price="+price); } } abstract class Coffee { /** * 返回价格 * * @return */ public abstract int getPrice(); /** * 返回名字 * * @return */ public abstract String getName(); } abstract class DecoratorCoffee extends Coffee { protected Coffee mCoffee; /** * 通过组合的方式把Coffee对象传递进来 * * @param coffee */ public DecoratorCoffee(Coffee coffee) { mCoffee = coffee; } } class SimpleCoffee extends Coffee { int price = 20; String simpleName = "黑咖啡"; @Override public int getPrice() { return price; } @Override public String getName() { return simpleName; } } class MilkDecorator extends DecoratorCoffee { String mileName = "加牛奶"; /** * 通过组合的方式把Coffee对象传递进来 * @param coffee */ public MilkDecorator(Coffee coffee) { super(coffee); } @Override public int getPrice() { return mCoffee.getPrice() + 10; } @Override public String getName() { return mCoffee.getName() + mileName; } } class SugarDecorator extends DecoratorCoffee { String sugarName = "加糖"; /** * 通过组合的方式把Coffee对象传递进来 * * @param coffee */ public SugarDecorator(Coffee coffee) { super(coffee); } @Override public int getPrice() { return mCoffee.getPrice() + 5; } @Override public String getName() { return mCoffee.getName() + sugarName; } }
代理模式:
代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的引用。它使得客户不能直接与真正的目标对象通信。代理对象是目标对象的代表,其他需要与这个目标对象打交道的操作都是和这个代理对象在交涉。
动态代理有别于静态代理,是根据代理的对象,动态创建代理类。这样,就可以避免静态代理中代理类接口过多的问题。动态代理是实现方式,是通过反射来实现的,借助Java自带的java.lang.reflect.Proxy
代理模式应用场景
SpringAop、日志收集、权限控制、过滤器、RPC远程调用
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author lin.hongwen * @date 2020-05-14 */ public class DynamicProxyMain { public static void main(String[] args) { Person person = new Child(); Person proxyBuyHouse = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[] { Person.class }, new DynamicProxyHandler(person)); proxyBuyHouse.eat(); } } interface Person { void eat(); } class Child implements Person { @Override public void eat() { System.out.println("Child eating"); } } class DynamicProxyHandler implements InvocationHandler { private Object object; public DynamicProxyHandler(final Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("调用前日志监控"); Object result = method.invoke(object, args); System.out.println("调用后日志监控"); return result; } }
观察者模式:
又称发布-订阅模式。场景如:我们都在新浪微博中关注过某一位明星,每当这位明星发布一条动态时候,他的粉丝就都会知道。这就是观察者模式
观察者模式的应用场景:
1、 对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
2、 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。
具体实现:
1.定义抽象观察者(Observer):抽象的粉丝
/** * @author lin.hongwen * @date 2020-05-17 * 定义抽象观察者(Observer):抽象的粉丝 */ public interface Fan { public void update(String message); }
2.具体的观察者(concreteObserver):具体的粉丝
/** * @author lin.hongwen * @date 2020-05-17 * 具体的观察者(concreteObserver):具体的粉丝 */ public class ConcreteFan implements Fan { private String fanName; public ConcreteFan(String fanName) { this.fanName = fanName; } @Override public void update(String message) { System.out.println(fanName + "收到了:" + message); } }
3.定义抽象主题(Subject):抽象明星
/** * @author lin.hongwen * @date 2020-05-17 * 定义抽象主题(Subject):抽象明星 */ public interface IdoSomething { /** * 增加粉丝 */ public void addFan(Fan fan); /** * 拉黑粉丝 */ public void deFan(Fan fan); /** * 告诉粉丝我的动态 */ public void notify(String message); }
4.具体主题(ConcreteSubject):具体的明星
import java.util.ArrayList; import java.util.List; /** * @author lin.hongwen * @date 2020-05-17 * 具体主题(ConcreteSubject):具体的明星 */ public class ConcreateIdoSomething implements IdoSomething { /** * 保存所有的粉丝 */ private List<Fan> fanList = new ArrayList<Fan>(); @Override public void addFan(Fan fan) { /** * 增加粉丝 */ fanList.add(fan); } @Override public void deFan(Fan fan) { /** * 拉黑粉丝 */ fanList.remove(fan); } @Override public void notify(String message) { for(Fan fan : fanList) { /** * 给每一个粉丝发送消息 */ fan.update(message); } } }
5.创建测试类:
/** * @author lin.hongwen * @date 2020-05-17 */ public class Test { public static void main(String[] args) { /** * 第一步:有一个明星 */ ConcreateIdoSomething concreateIdoSomething = new ConcreateIdoSomething(); /** * 第二步:有很多粉丝,这里给出3个 */ ConcreteFan fanA = new ConcreteFan("粉丝A"); ConcreteFan fanB = new ConcreteFan("粉丝B"); ConcreteFan fanC = new ConcreteFan("粉丝C"); /** * 第三步:让粉丝去关注明星(反过来,明星把他们拉进来) */ concreateIdoSomething.addFan(fanA); concreateIdoSomething.addFan(fanB); concreateIdoSomething.addFan(fanC); /** * 第四步:明星发动态,粉丝获取动态 */ concreateIdoSomething.notify("大家好,我是菜徐鲲,我会唱、跳、Rap、篮球"); /** * 第五步:让粉丝取消关注(反过来,明星拉黑粉丝) */ concreateIdoSomething.deFan(fanB); /** * 第六步:明星重新发动态,粉丝重新获取动态 */ concreateIdoSomething.notify("菜徐鲲在打乒乓球"); } }
结果: