public interface ICourse{
Integer getId();
String getName();
Double getPrice();
}
public class JavaCourse implements ICourse{
private Integer Id;
private String name;
private Double price;
public JavaCourse(Integer id, String name, Double price){
this.Id = id;
this.name = name;
this.price = price;
}
public Integer getId(){
return this.Id;
}
public String getName(){
return this.name;
}
public Double getPrice(){
return this.price;
}
}
public class JavaDiscountCourse extends JavaCourse{
public JavaDiscountCourse(Integer id, String name, Double price){
super(id,name,price);
}
public Double getOriginPrice(){
return super.getPrice();
}
@Override
public Double getPrice(){
return super.getPrice()*0.8;
}
}
public class Test{
public static void main(String[] args){
ICourse iCourse = new JavaDiscountCourse(11,"语文课",300d);
JavaDiscountCourse javaCourse = (JavaDiscountCourse)iCourse;
System.out.println("课程ID"+javaCourse.getId() + "课程名称:"+ javaCourse.getName()+"课程原价:"+javaCourse.getPrice());
}
}
public class Geely{
public void studyJavaCourse(){
System.out.ptintln("Geely在学习Java课程");
}
public void studyFECourse(){
System.out.println("Geely在学习前端课程");
}
}
public class Test{
Geely geely = new Geely();
geely.studyJavaCourse();
geely.studyFECourse();
}
public class Geely{
public void studyJavaCourse(){
System.out.ptintln("Geely在学习Java课程");
}
public void studyFECourse(){
System.out.println("Geely在学习前端课程");
}
public void studyPythonCourse(){
System.out.println("Geely在学习Python课程");
}
}
我们发现,每当我们上级想需要一个功能的时候,是需要低层来实现的,它的稳定性很差。根据依赖导致原则,高层次的模块是不应该依赖低层的模块的,这不符合依赖倒置原则,如何解决呢?使用抽象来改进代码。
public interface ICourse{
void studyCourse();
}
public class JavaCourse implements ICourse{
@Override
public void studyCourse(){
System.out.println("Geely在学习Java课程");
}
}
public class FECourse implements Icourse{
@Override
public void studyCourse(){
System.out.println("Geely在学习FE课程");
}
}
public class Geely{
public void studyCourse(Icourse icourse){
iCourse.studyCourse();
}
}
public class Test{
public static void main(String[] args){
Geely geely = new Geely();
geely.studyCourse(new JavaCourse());
geely.studyCourse(new FECourse());
}
}
使用了抽象,我们可以根据需要创建各种不同的实现类,既达到了接口、保持了接口功能的稳定性,同时遵从了依赖倒置原则,上层应用不再依赖Geely这个类了,而是根据需要去实现对应的Course类;
public class Geely{
private ICourse iCourse;
public Geely(ICourse iCourse){
this.iCourse = iCourse;
}
public void studyCourse(ICourse iCourse){
iCourse.studyCourse();
}
}
public class Test{
public static void main(String[] args){
Geely geely = new Geely(new JavaCourse());
geely.studyImooCourse();
}
}
使用构造函数注入虽然避免了里面的方法需要单独引入接口,但是构造注入会导致只能使用一种课程,假如Geely要同时学习Java和前端课程,那就得需要创建两个类了,这显然不是我们想要的,使用set注入能够解决这一问题。
public class Geely{
public void setCourse(ICourse iCourse){
this.iCourse = iCourse;
}
private ICourse iCourse;
public void studyCourse(){
iCourse.studyCourse();
}
}
在低层次模块我们可以进行扩展,在上层模块中就满足了依赖导致原则。
public class Bird{
public mainMoveMode(String birdName){
if("鸵鸟".equals(birdName)){
System.out.println(birdName+"用脚走");
}else{
System.out.println(birdName+"用翅膀飞");
}
}
}
例子中没有使用单一职责,如果出现了别的情况,比如某某鸟儿爬着走,某某鸟儿跳着走,是不是又要加很多if else 判断了?我们可以单一职责来解决这个问题,比如说创建一种飞着走的鸟儿类,创建一种用脚走的鸟儿类,这样对应的鸟儿可以使用对应的动作类,就可以完成功能,同时不会造成耦合度过高,同时满足了单一职责;
public interface ICourse{
String getCourseName();
byte[] getCourseVideo();
void studyCourse();
void refundCourse();
}
这个接口存在的问题就是:上面是获取课程的两个方法,而下面的是学习课程和对课程退款的功能。一个接口中定义了不同职责的方法,会导致混乱。比如一个学习类,我只想实现getCourseName()方法和getCourseVideo()方法,但是继承了这个接口后必须全部实现,造成了职责不单一。
我们在设计类的过程中,应该也要考虑以后可能会发生变化的地方,做一些预判。
我们发现了一个问题,狗实现类动物接口,但是它不能实现fly方法,因为狗不会飞。同时鸟儿实现了swim方法,但是鸟儿不会游泳。这说明了没有实现接口隔离原则,导致了有一些方法对于实现类来说不适用。
细化接口要适度,避免类膨胀;
理论:
coding:
public class Boss{
public void commandCheckNumber(TeamLeader temLeader){
List<Course> courseList= new ArrayList<Course>();
for(int i = 0; i<20 ; i++){
courseList.add(new Course());
}
teamLeader.checkNumberOfCourses(courseList);
}
}
public class TeamLeader{
public void checkNumberOfCourses(List<Course> courseList){
System.out.println("在线课程的数量是CSDN暗余:"+courseList.size());
}
}
public class Boss{
public void commandCheckNumber(TeamLeader teamLeader){
teamLeader.checkNumberOfCourse();
}
}
public class TeamLeader{
public void checkNumberOfCourses(){
List<Course> courseList= new ArrayList<Course>();
for(int i = 0; i< 20; i++){
courseList.add(new Course());
}
System.out.println("在线课程的数量是:"+courseList.size());
}
}
Boss想从TeamLeader那里知道现有课程的总数。它们之间的调用关系应该为Boss—>TeamLeader—>Course。Boss与Course并无直接联系,所以在Boss类的方法中不应该出现Course类。这就是迪米特法则;
定义与类型:
简单工厂-适用场景:
简单工厂优点:
简单工厂缺点:
使用简单工厂前的准备工作:
public abstract class Video{
public abstract void product();
}
public class JavaVideo extends Video{
@Override
public void produce(){
System.out.println("录制Java课程视频");
}
}
public class PythonVideo extends Video{
@Override
public void produce(){
System.out.println("录制Python课程视频");
}
}
public class Test{
public static void main(String[] args){
Video video = new PythonVideo();
video.produce();
}
}
从测试类中,我们可以发现,每当我们需要PythonVideo类或者JavaVideo类时,会手动New创建一个,这会导致我们将创建逻辑显式的展示在我们的场景类中。我们可以将创建过程移植到简单工厂中,改进看下面。
使用简单工厂进行改造:
public class VideoFactory{
public Video getVideo(String type){
if("java".equalsIgnoreCase(type)){
return new JavaVideo();
}else if("python".equalsIgnoreCase(type)){
return new PythonVideo();
}
return null;
}
}
public class Test{
public static void main(String[] args){
VideoFactory videoFactory = new VideoFactory();
Video video = videoFacotry.getVideo("java");
if(video == null){
return;
}
video.produce();
}
}
通过简单工厂类,我们将创建类的具体实现细节隐藏掉了,通过一个工厂类就可以创建多种不同类型的类。但是存在一个问题,那就是创建类的时候不够灵活,每当需要一个全新的类的时候,就会要去修改工厂类中的创建类方法,所以它违背了开闭原则。
对工厂类进行升级改造(通过工厂+ 反射的方式来增强扩展性):
public class VideoFactory{
public Video getVideo(Class c){
Video video = null;
try{
video = (Video)Class.forName(c.getName()).newInstance();
}catch(InstantiationException e){
e.printStackTrace();
}catch(IllegalAccessException e){
e.printStackTrace();
}catch(ClassNotFoundException e){
e.printStackTrace();
}
return video;
}
}
public class Test{
public static void main(String[] args){
VideoFactory videoFactory = new VideoFactory();
Video video = videoFacotry.getVideo(JavaVideo.class);
if(video == null){
return;
}
video.produce();
}
}
通过反射的方式增强了扩展性,即使增加了新的类型,但是只要它属于Video接口下的对象,那么就能够创建成功,且不需要对既有代码进行修改。
简单工厂源码分析:
public abstract class VideoFactory{
public abstract Video getVideo();
}
在抽象工厂类中,我们使用的是abstract抽象类,而不是接口,是因为我们在这个类中可能会存在一些已知的方法,而接口类只能定义接口不能实现具体的逻辑,所以此处使用的是抽象类。
public abstract class Video {
public abstract void produce();
}
public class JavaVideoFactory extends VideoFactory{
@Override
public Video getVideo(){
return new JavaVideo();
}
}
public class PythonVideoFactory extends VideoFactory{
@Override
public Video getVideo(){
return new PythonVideo();
}
}
public class Test{
public static void main(String[] args){
VideoFactory videoFactory = new PythonVideoFactory();
Video video = videoFactory.getVideo();
video.produce();
}
}
FEVideo 类与PythonVideo 类似,创建的一个新的类。其中都继承于Video抽象类,而工厂构造类都继承于VideoFactory,在Test类中需要创建具体的类的时候,只需要构造一个工厂类指向对应的子类即可。
工厂方法就是工厂类的一个方法,它是一个抽象方法,具体实现由其子类实现;
里面的Itr依赖于Iterator 接口类
抽象工厂-定义与类型
抽象工厂-适用场景:
抽象工厂- 优点
抽象工厂- 缺点
抽象工厂 - 产品等级结构与产品族
为每个类创建一个工厂类,不是一个很好的设计。
public interface CourseFactory{
Video getVideo();
Article getArticle();
}
public abstract class Article{
public abstract void produce();
}
public abstract class Video{
public abstract void produce();
}
public class JavaCourseFactory implements CourseFactory{
@Override
public Video getVideo(){
return new JavaArticle();
}
@Override
public Article getArticle(){
return new JavaArticle();
}
}
public class JavaArticle extends Article{
@Override
public void produce(){
System.out.println("编写Java课程笔记");
}
}
public class JavaVideo extends Video{
@Override
public void produce(){
System.out.println("录制Java课程视频");
}
}
在Java的jdbc中,就大量使用了抽象工厂。建立连接的步骤是一致的,但是被操作的对象却各不相同;比如mysql、sqlserver等各种数据库通过对抽象的各自实现,让我们无感知的能够连接各自的的服务器;
建造者模式和工厂模式相同的地方都是用于创建对象,但是不同的地方是工厂模式更加注重于创建产品,而建造者模式更注意创建的顺序;建造者可以创建复杂的产品,可以控制顺序,而工厂模式创建出来的每个对象都是一样的(它不注重顺序及组成);
public class Course{
private String courseName;
private String coursePPT;
private String courseVideo;
private String courseArticle;
private String courseQA;
public Course(CourseBuilder courseBuilder){
this.courseName = courseBuilder.courseName;
this.coursePPT = courseBuilder.coursePPT;
this.courseVideo = courseBuilder.courseVideo;
this.courseArticle = courseBuilder.courseArticle;
this.courseQA = courseBuilder.courseQA;
}
// 创建静态类,用于链式调用+建造者
public static class CourseBuilder{
private String courseName;
private String coursePPT;
private String courseVideo;
private String courseArticle;
private String courseQA;
public CourseBuilder buildCourseName(String courseName){
this.courseName = courseName;
return this;
}
public CourseBuilder buildCoursePPT(String coursePPT){
this.coursePPT = coursePPT;
return this;
}
public CourseBuilder buildCourseVideo(String courseVideo){
this.courseName = courseVideo;
return this;
}
public CourseBuilder buildCourseArticle(String courseArticle){
this.courseName = courseArticle;
return this;
}
public CourseBuilder buildCoursQA(String courseQA){
this.courseName = courseQA;
return this;
}
public Course build(){
return new Course(this);
}
}
}
public class Test{
public static void main(String[] args){
Course course = new Course.CourseBuilder().buildCourseName("java课程").buildCoursQA("问题和答案").build();
System.out.println(course);
}
}
源码中StringBuilder/StringBuffer 使用到了建造者模式,它可以一直append 字符串;同时ImmutableSet中的add方法也是建造者模式,如: Set set = ImmutableSet.builder().add(“a”).add(“b”).build();
public class LazySingleton{
private static LazySingleton lazySingleton = null;
private LazySingleton(){
}
public static LazySingleton getInstance(){
if(lazySingleton == null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
有线程安全问题,但是实现了延时加载的功能;
public class LazySingleton{
private static LazySingleton lazySingleton = null;
private LazySingleton(){
}
public static synchronized LazySingleton getInstance(){
if(lazySingleton == null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
这种方式实际上就是加了一个synchronized关键字,使用同步的方式虽然避免了线程不安全,但是直接在静态方法上面加上synchronized的话,锁是锁住整个对象的。
public class LazySingleton{
private volatile static LazySingleton lazySingleton = null;
private LazySingleton(){
}
public static LazySingleton getInstance(){
if(lazySingleton == null){
synchronized(LazySingleton.class){
if(lazySingleton == null){
lazySingleton = new LazySingleton();
}
}
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
第一个判空出现在synchronized 的话,只会当实例为空的时候才有可能会阻塞,提高了性能;第二次判空是在加锁之后,这里面加锁比在方法上加锁性能更好。而LazySingleton 定义的时候使用了volatile关键字,是为了此实例不会出现重排序的现象;因为在计算机里面,指令个为了保证性能不一定会是依次执行,而new 关键字在底层可以理解为经历了三个步骤:1. 分配内存给这个对象,初始化对象,设置变量指向刚分配的内存对象,如果2步骤和3步骤发生了顺序调换,很可能在synchronnized外可能会获取到一个空引用;
public class StaticInerClassSingleton{
private static class InnerClass{
private static StaticInnerClassSingLeton staticInnerClassSingleton = new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance(){
return InnerClass.staticInnerClassSingleton;
}
}
public class HungrySingleton{
private final static HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
public class HungrySingleton{
private final static HungrySingleton hungrySingleton;
static {
hungrySingleton = new HungrySingleton();
}
private HungrySingleton(){
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
懒汉与饿汉的区别就在于是否是延迟加载;
问题:
解决方案:
public enum EnumbInstance{
INSTANCE;
private Object data;
public Object getData(){
return data;
}
public void setData(Object data){
this.data = data;
}
public static EnumInstance getInstance(){
return INSTANCE;
}
}
public class ContainerSingleton{
private ContainerSingleton(){}
private static Map<Stirng,Object> singletonMap = new HashMap<>();
public static void putInstance(String key,Object instance){
if(StringUtils.isNotBlank(key) && instance != null){
if(!singletonMap.containKey(key)){
singletonMap.put(key,instance);
}
}
}
publiuc static Object getInstance(String key){
return singletonMap.get(key);
}
}
如果存入相同key的value,HashMap会更新为最后一个相同key的value,而HashMap是线程不安全的,要注意此种情况;
public class ThreadLocalInstance{
private static final ThreadLocal<ThreadLocalInstance> threadLocalInstanceThreadLocal = new ThreadLocal<ThreadLocalInstance>(){
@Override
protected ThreadLocalInstance initialValue(){
return new ThreadLocalInstance();
}
};
private ThreadLocalInstance(){}
public static ThreadLocalInstance getInstance(){
return threadLocalInstanceThreadLocal.get();
}
}
@Data
@ToString
public class Mail implements Cloneable{
private String name;
private String emailAddress;
private String content;
public Mail(){}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
public class Test{
public static void main(String[] args) throws CloneNotSupportedException{
Mail mail = new Mail();
mail.setContent("初始化模板");
for(int i = 0; i < 10; i++){
Mail mailTemp = (Mail)mail.clone();
mailTemp.setName("姓名"+i);
mailTemp.setEmailAddress("姓名"+ i + "@imooc.com");
mailTemp.setContent("恭喜您,此次活动中奖了");
MailUtil.sendMail(mailTemp);
}
}
}
当我们需要频繁的创建一个相同对象,且前期需要大量准备,则我们可以考虑使用原型模式;
public abstract class A implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
public class B extends A{
public static void main(String[] args) throw CloneNotSupportedException{
B b = new B();
b.clone();
}
}
当B使用克隆方法时,会使用抽象A类中的clone方法中去;打断点需要注意哦;
如果一个单例对象继承了克隆方法,那么克隆方法存在破坏单例的风险;要么单例对象不实现克隆方法,要么在里面还是返回我们单例的实例;还有注意设计模式不是单打独斗,要灵活使用,可以多模式混用,具体与现实场景有关;
在ArrayList、HashMap中都有用到原型模式,可以在源码里面找到Clone 方法,他们都实现了Cloneable接口;感兴趣可以看看。
外观模式是迪米特法则的充分提现,即最少知道原则,降低了系统之间的耦合度,提升内聚;
public class ModuleA {
//示意方法
public void testA(){
System.out.println("调用ModuleA中的testA方法");
}
}
public class ModuleB {
//示意方法
public void testB(){
System.out.println("调用ModuleB中的testB方法");
}
}
public class ModuleC {
//示意方法
public void testC(){
System.out.println("调用ModuleC中的testC方法");
}
}
public class Facade {
//示意方法,满足客户端需要的功能
public void test(){
ModuleA a = new ModuleA();
a.testA();
ModuleB b = new ModuleB();
b.testB();
ModuleC c = new ModuleC();
c.testC();
}
}
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.test();
}
}
使用门面模式还有一个好处,就是能够选择性的暴露方法。 一个模块中定义的方法可以分为两部分,一部分是给子系统外部使用的,一部分是子系统内部模块之间相互调用时使用的。有了Facade类,那么用于子系统内部模块之间相互调用的方法就不用暴露给子系统外部了。
public class Battercake{
protected String getDesc(){
return "煎饼";
}
protected int cost(){
return 8;
}
}
被protected 修饰的属性,只允许在子类中重写;
public class BattercakeWithEgg extends Battercake{
@Override
public String getDesc(){
return super.getDesc()+" 加一个鸡蛋";
}
@Override
public int cost(){
return super.cost()+1;
}
}
public class BattercakeWithEggSausage extends BattercakeWithEgg{
@Override
public String getDesc(){
return super.getDesc() +" 加一根香肠";
}
@Override
public int cost(){
return super.cost();
}
}
因为继承了加了鸡蛋的煎饼类,所以它会**“先加鸡蛋再加香肠”**;
public class Test{
public static void main(String[] args){
Battercake battercake = new Battercake();
System.out.println(battercake.getDesc() + " 销售价格:"+ battercake.getCost());
Battercake battercake2 = new BattercakeWithEgg();
System.out.println(battercake2.getDesc() + " 销售价格:"+ battercake2.getCost());
Battercake battercake3 = new BattercakeWithEggSausage();
System.out.println(battercake3.getDesc() + " 销售价格:"+ battercake3.getCost());
}
}
我们发现,使用类继承能够实现我们需要的业务需求;但是根据实际来看,顾客往往可能还需要三个鸡蛋的煎饼、五个火腿肠的煎饼、各种的随意组合,如果我们单纯使用类继承,是无法办到的,它既导致了类过于膨胀,且不具备扩展性;接下来我们看看装饰者模式是否能够解决这个问题!
public abstract class Abattercake{
protected abstract String getDesc();
protected abstract int cost();
}
public class Battercake extends Abattercake{
@Override
protected String getDesc(){
return "煎饼";
}
@Override
protected int cost(){
return 8;
}
}
public class AbstractDecorator extends Abatterrcake{
private Abattercake aBattercake;
public AbstractDecorator(Abattercake aBattercake){
this.aBattercake = aBattercake;
}
@Override
protected String getDesc(){
return this.aBattrercake.getDesc();
}
@Override
protected int cost(){
return this.aBattercake.cost();
}
}
public class SausageDecorator extends AbstractDecorator{
public SausageDecorator(Abattercake aBattercake){
super(aBattercake);
}
@Override
protected String getDesc(){
return super.getDesc()+" 加一个香肠";
}
@Override
protected int cost(){
return super.cost()+2;
}
}
public class EggDecorator extends AbstractDecorator{
public EggDecorator(ABattercake aBattercake){
super(aBattercake);
}
@Override
protected String getDesc(){
return super.getDesc()+" 加一个鸡蛋";
}
@Override
protected int cost(){
return super.cost() +1;
}
}
public class Test{
public static void main(String[] args){
Abattercake aBattercake;
// 要一个煎饼
aBattercake = new Battercake();
// 加一个鸡蛋
aBattercake = new EggDecorator(aBattercake);
// 再加一个鸡蛋
aBattercake = new EggDecorator(aBattercake);
// 再加一个火腿肠
aBattercake = new SausageDecorator(aBattercake);
System.out.println(aBattercake.getDesc() +" 销售价格:"+aBattercake.cost());
}
}
我们使用了装饰者抽象类、同时利用继承关系,将对象不断的传入,然后进行累加,能够动态的控制煎饼、火腿肠和煎饼,既实现了需求,又满足了未来扩展需要,同时使代码更加简洁高效;在一些我们常用的工具中,InputStream拥有很多的子类,它也实现了装饰者模式;他们通过实现要被装饰的类,来扩展一些类的功能,同时不会对原有类造成侵入;
创建被适配者Adaptee:
public class Adaptee{
public void adapteeRequest(){
System.out.println("被适配者的方法");
}
}
定义请求接口Target:
public interface Target{
void request();
}
定义实现类,实现Target:
public class ConcreteTarget implements Target{
@Override
public void request(){
System.out.println("concreateTarget目标方法");
}
}
定义适配器类,通过继承 Target 接口,从而能够同步request方法,同时继承Adaptee,获取到它内部的adapteeRequest()方法,这样就能够在request内部去调用adapteeRequest方法,达到适配的效果:
public class Adapter extends Adaptee implements Target{
@Override
public void request(){
super.adapteeRequest();
}
}
类结构如图所示:
创建测试类,测试功能:
public class Test{
public static void main(String[] args){
Target target = new ConcreteTarget();
target.request();
Target adapterTarget = new Adapter();
adapterTarget.request();
}
}
通过实现同一个接口,可以达到调用同一个方法的目的,而在内部继承被适配的类,可以将被适配的类的方法在适配器类中调用,达到适配的效果;
public class ConcreteTarget implements Target{
@Override
public void request(){
System.out.println("concreateTarget目标方法");
}
}
public class Adaptee{
public void adapteeRequest(){
System.out.println("被适配者的方法");
}
}
public class ConcreteTarget implements Target{
@Override
public void request(){
System.out.println("concreateTarget目标方法");
}
}
以上三个跟类适配器中一样,没有发生变化,发生变化的是适配器本身;
public class Adapter implements Target{
private Adaptee adaptee = new Adaptee();
@Override
public void request(){
adaptee.adapteeRequest();
}
}
public class Test{
public static void main(String[] args){
Target target = new ConcreteTarget();
target.request();
Target adapterTarget = new Adapter();
adapterTarget.request();
}
}
public class AC220{
public int outputAC220V(){
int output = 220;
System.out.println("输出交流电"+output+"伏");
return output;
}
}
public interface DC5{
int outputDC5V();
}
public class PowerAdapter implements DC5{
private AC220 ac220 = new AC220();
@Override
public int outputDC5V(){
int adapterInput = ac220.outputAC220V();
// 变压器
int adapterOutput = adapterInput/44;
System.out.println("使用PowerAdapter输入AC:"+ adapterInput +"V");
return adapterOutput;
}
}
定义与类型:
一句话概括:减少对象创建,提高性能;
适用场景:
享元-优点:
享元-缺点:
享元-扩展:
享元-相关设计模式:
享元模式就是一个代码复用的思想,线程池就是其思想的一个实现之一;
public interface Employee{
void report();
}
public class Manager implements Employee{
@Override
public void report(){
System.out.println(reportContent);
}
private String department;
private String reportContent;
public void setRepoirtContent(String reportContent){
this.reprotContent = reportContent;
}
public Manager(String department){
this.department = department;
}
}
public class EmployeeFactory{
private static final Map<String,Employee> EMPLOYEE_MAP = new HashMap<>();
public static Employee getManager(String department){
Manager manager = (Manager) EMPLOYEE_MAP.get(department);
if(manager == null){
manager = new Manager(department);
manager.setReportContent(department+"部门汇报:内容是...");
EMPLOYEE_MAP.put(department,manager);
}
return manager;
}
}
public class Test{
private static final String departments[] = {"RD","QA","PM","ET"};
public static void main(String[] args){
for(int i = 0 ; i < 10 ; i++){
String department = departments[(int)Math.random()];
Manager manager = (Manager) EmployeeFactory.getManager(department);
manager.report();
}
}
}
使用享元模式的思想,类只会被创建一次;
这里也是一个面试题,当两个Integer 大于127的时候,他们则不会相等;而在范围内时则是一个对象;
概述:
图示:
适用场景:
组合-优点:
组合-缺点:
public abstract class CatalogComponent{
public void add(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持添加操作!");
}
public void remove(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持删除操作!");
}
public String getName(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持获取名称操作!");
}
public double getPrice(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持获取价格操作!");
}
public void print(){
throw new UnsupportedOperationException("不支持打印操作!")
}
}
public class Course extends CatalogComponent{
private String name;
private double price;
public Course(String name, double price){
this.name = name;
this.price = price;
}
@Override
public String getName(CatalogComponent catalogComponent){
return this.name;
}
@Override
public String getPrice(CatalogComponent catalogComponent){
return this.price;
}
@Override
public void print(){
System.out.println("Course Name:"+name+" Price:"+price);
}
}
public class CourseCatalog extends CatalogComponent{
private List<CatalogComponent> items = new ArrayList<CatalogComponent>();
private String name;
public CourseCatalog(String name){
this.name = name;
}
@Override
public void add(CatalogComponent catalogComponent){
items.add(catalogComponent);
}
@Override
public void remove(CatalogComponent catalogComponent){
items.remove(catalogComponent);
}
@Override
public void print(){
for(CatalogComponent catalogComponent : items){
catalogComponent.print();
}
}
@Override
public void getName(CatalogComponent catalogComponent){
return this.name;
}
}
子类没有实现父类的方法的时候,会自动执行父类的方法;
public class Test{
public static void main(String[] args){
// 父子关系
CatalogComponent linuxCourse = new Course("Linux课程",11);
CatalogComponent windowsCourse = new Course("Windows课程",11);
CatalogComponent javaCourseCatalog = new CourseCatalog("Java课程目录",11);
javaCousrseCatalog.add(linuxCourse);
javaCousrseCatalog.add(windowsCourse);
System.out.println(JSON.toJSONString(javaCourseCatalog));
// 平级关系
CatalogComponent parentCatalog = new CourseCatalog("暗余视频学习网");
parentCatalog.add(linuxCourse);
parentCatalog.add(windowsCourse);
parentCatalog.add(javaCourseCatalog);
parentCatalog.print();
}
}
通过如图所示可以用以查看UML类图,对于理解源码的结构很有帮助!
UML:
通过此功能可以查看此接口下的实现类;
代码:
在SQL NODE中,每个Node的功能不尽相同,通过类似如上的方式可以将它们组合起来,这就是组合模式的妙用;
概述:
可以防止类爆炸;
适用场景:
优点:
缺点:
相关设计模式:
public interface Account{
Account openAccount();
void showAccountType();
}
public class DepositAccount implements Account{
@Override
public Account openAccount(){
System.out.pintln("打开定期账号");
return new DepositAccount();
}
@Override
public void showAccountType(){
System.out.println("这是一个定期账号");
}
}
public class SavingAccount implements Account{
@Override
public Account openAccount(){
System.out.println("打开活期账号")
return new SavingAccount();
}
@Override
public void showAccountType(){
System.out.println("这是一个活期账号");
}
}
public abstract class Bank{
protected Account account;
public Bank(Account account){
this.account = account;
}
abstract Account openAccount();
}
public class ABCBank extends Bank{
public ABCBank(Account account){
super(account);
}
@Override
Account openAccount(){
System.out.println("打开中国农业银行账号")
return account;
}
}
public class ICBCBank extends Bank{
public ICBCBank(Account account){
super(account);
}
@Override
Account openAccount(){
System.out.println("打开中国工商银行账号");
return account;
}
}
最简单的方式是继承的方案,但是它可扩展较低,不符合迪米特法则。通过桥接模式,我们可以将一个接口的实现类注入到操作类中,这样可以根据接口来将两个桥接起来;达到高内聚低耦合的目的;
public class Test{
public static void main(String[] args){
Bank icbcBank = new ICBCBank(new DepositAccount());
Account icbcAccount = icbcBank.openAccount();
icbcAccount.showAccountType();
Bank abcBank = new ABCBank(new SavingAccount());
Account abcAccount = abcBank.openAccount();
abcAccount.showAccountType();
}
}
我们可以将一些每个银行不同的业务逻辑以及账号的业务逻辑写在各自的实现类中,这样就可以直接调用了;
public interface IUserDao{
public 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;
}
@Override
public void save(){
System.out.println("开启事务");
target.save();
System.out.println("提交事务");
}
}
import org.junit.Test;
public class StaticUserProxy{
@Test
public void testSaticProxy{
// 目标对象
IUserDao target = new UserDao();
// 代理对象
UserDaoProxy proxy = new UserDaoProxy(target);
proxy.save();
}
}
public interface IUserDao{
public void save();
}
public class UserDao implements IUserDao{
@Override
public void save(){
System.out.println("保存数据");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
private Object target;// 维护一个目标对象
public ProxyFactory(Object target) {
this.target = target;
}
// 为目标对象生成代理对象
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("开启事务");
// 执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务");
return null;
}
});
}
}
import org.junit.Test;
public class TestProxy{
@Test
public void testDynamicProxy(){
IUserDao target = new UserDao();
System.out.println(target.getClass()); // 输出目标对象信息
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
System.out.println(proxy.getClass()); // 输出代理对象信息
proxy.save(); // 执行代理方法
}
}
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
public class UserDao{
public void save(){
System.out.println("保存数据");
}
}
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ProxyFactory implements MethodInterceptor{
private Object target;//维护一个目标对象
public ProxyFactory(Object target) {
this.target = target;
}
//为目标对象生成代理对象
public Object getProxyInstance() {
//工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperclass(target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类对象代理
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开启事务");
// 执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("关闭事务");
return null;
}
}
public class TestProxy{
@Test
public void testCglibProxy(){
// 目标对象
UserDao target = new UserDao();
System.out.println(target.getClass());
// 代理对象
UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
System.out.println(proxy.getClass());
// 执行代理对象方法
proxy.save();
}
}
业务分析:首先我们需要定义一个
public abstract class ACourse{
protected final void makeCourse(){
this.makePPT();
this.makeVideo();
if(needWriteArticle()){
this.writeArticle();
}
this.packageCourse();
}
final void makePPT(){
System.out.println("制作PPT");
}
final void makeVido(){
System.out.println("制作视频");
}
final void writeArticle(){
System.out.println("编写文章");
}
// 钩子方法
protected boolean needWriteArticle(){
return false;
}
// 素材 这个接口留给实现类来实现
abstract void packageCourse();
}
比如这个素材,可能不同课程的素材是不同的,这个可以留给实现类去实现;needWriteArticle这个方法会返回true或者false,我们默认是false所以不执行,如果实现类重写了这个方法并返回了true,则会执行writeArticle 这个功能;
public class DesignPatternCourse extends ACourse{
@Override
void packageCourse(){
System.out.println("提供课程Java源代码");
}
@Override
protected boolean needWriteArticle(){
return true;
}
}
public class FECourse extends ACourse{
@Override
void packageCourse(){
System.out.println("提供课程的前端代码");
System.out.println("提供课程的图片等多媒体素材");
}
}
public class Test{
public static void main(String[] args){
System.out.println("后端设计模式课程 start ---");
ACourse designPatternCourse = new DesignPatternCourse();
designPatternCourse.makeCourse();
System.out.println("后端设计模式课程end ---");
System.out.println("前端课程start ---");
ACourse feCourse = new FECourse();
feCourse.makeCourse();
System.out.println("前端课程end ---");
}
}
public class FECourse extends ACourse{
private boolean needWriteArticleFlag = false;
@Override
void packageCourse(){
System.out.println("提供课程的前端代码");
System.out.println("提供课程的图片等多媒体素材");
}
public FECourse(boolean needWriteArticleFlag){
this.needWriteArticleFlag = needWriteArticleFlag;
}
@Override
protected boolean needWriteArticle(){
return this.needWriteArticleFlag;
}
}
public class Course {
private String name;
public Course(String name) {
this.name = name;
}
public String getName(){
return name;
}
}
public interface CourseAggregate {
void addCourse(Course course);
void removeCourse(Course course);
CourseIterator getCourseIterator();
}
这个接口定义了三个方法,分别是新增课程,删除课程,获取这个课程的迭代器;
public interface CourseIterator{
Course nextCourse();
boolean isLastCourse();
}
public class CourseAggregateImpl implements CourseAggregate {
private List courseList;
public CourseAggregateImpl(){
this.courseList = new ArrayList();
};
@Override
public void addCourse(Course course){
courseList.add(course);
};
@Override
public void removeCourse(Course course){
courseList.remove(course);
};
@Override
public CourseIterator getCourseIterator(){
return new CourseIteratorImpl(courseList);
}
}
public class CourseIteratorImpl implements CourseIterator{
private List courseList;
int position;
Course course;
public CourseIteratorImpl(List courseList){
this.courseList = courseList;
}
@Override
public Course nextCourse(){
System.out.println("返回课程,位置是:"+ position);
course = (Course) courseList.get(position);
position++;
return course;
}
@Override
public boolean isLastCourse(){
if(position < courseList.size()){
return false;
}
return true;
}
}
public class Test {
public static void main(String[] args){
Course course1 = new Course("Java 电商一期");
Course course2 = new Course("Java 电商二期");
Course course3 = new Course("Java 设计模式精讲");
Course course4 = new Course("Python课程");
Course course5 = new Course("算法课程");
Course course6 = new COurse("前端课程");
// 声明一个集合课程,并往里面添加课程
CourseAggregate courseAggregate = new CourseAggregateImpl();
courseAggregate.addCourse(course1);
courseAggregate.addCourse(course2);
courseAggregate.addCourse(course3);
courseAggregate.addCourse(course4);
courseAggregate.addCourse(course5);
courseAggregate.addCourse(course6);
System.out.println("----课程列表----");
printCourses(courseAggregate);
courseAggregate.removeCourse(course4);
courseAggregate.removeCourse(course5);
System.out.println("------删除操作之后的课程列表----");
printCourses(courseAggregate);
}
public static void printCourses(CourseAggregate courseAggregate){
CourseIterator courseIterator = courseAggregate.getCourseIterator();
while(!courseIterator.isLastCourse()){
Course course = courseIterator.nextCourse();
System.out.println(course.getName());
}
}
}
在源码中,List 集合就有迭代器模式,每个List 的实现类就有对应的迭代器实现;
public interface PromotionStrategy{
void doPromotion();
}
public class FanXianPromotionStrategy implements PromotionStrategy{
@Override
public void doPromotion(){
System.out.println("返现促销,返回的金额存放到用户的余额中");
}
}
public class LiJianPromotionStrategy implements PromotionStrategy{
@Override
public void doPromotion(){
System.out.println("立减促销,课程的价格直接减去配置的价格");
}
}
public class ManJianPromotionStrategy implements PromotionStrategy{
@Override
public void doPromotion(){
System.out.println("满减促销,满200-20元");
}
}
public class PromotionActivity{
private PromotionStrategy promotionStrategy;
public PromotionActivity(PromotionStrategy promotionStrategy){
this.promotionStrategy = promotionStrategy;
}
public void executePromotionStrategy(){
promotionStrategy.doPromotion();
}
}
public class Test{
public static void main(String[] args){
PromotionActivity promotionActivity618 = new LiJianPromotionStrategy();
PromotionActivity promotionActivity1111 = new FanXianPromotionStrategy();
promotionActivity618.executePromotionStrategy();
promotionActivity1111.executePromotionStrategy();
}
}
如图所示,当包含LIJIAN时,可能会执行立减的策略,依次类推,如果策略类很多的时候,if else 会带来代码臃肿,难以维护的问题;
public class PromotionStrategyFactory{
private static Map<String,PromotionStrategy> PROMOTION_STARTEGY_MAP = new HashMap<String,PromotionStrategy>();
private static final PromotionStrategy NON_PROMOTION = new EmptyPromotionStrategy();
static{
PROMOTION_STARTEGY_MAP.put(PromotionKey.LIJIAN,new LiJianPromotionStrategy());
PROMOTION_STARTEGY_MAP.put(PromotionKey.FANXIAN,new FanXianPromotionStrategy());
PROMOTION_STARTEGY_MAP.put(PromotionKey.MANJIAN,new ManJianPromotionStrategy());
}
private PromotionStrategyFactory(){
}
public static PromotionStrategy getPromotionStrategy(String promotionKey){
PromotionStrategy promotionStrategy = PROMOTION_STRATEGY_MAP.get(promotionKey);
return promotionStrategy == null ? NON_PROMOTION : promotionStrategy;
}
private interface PromotionKey{
String LIJIAN = "LIJIAN";
String FANXIAN = "FANXIAN";
String MANJIAN = "MANJIAN";
}
}
public class EmptyPromotionStrategy implements PromotionStrategy{
@Override
public void doPromotion(){
System.out.println("无促销活动");
}
}
public class Test{
public static void main(String[] args){
String promotionKey = "MANJIANxxx";
PromotionActivity promotionActivity = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategy(promotionKey));
promotionActivity.executePromotionStrategy();
}
}
在很多开源框架源码中应用到了很多的策略模式,比如比较器,List 的Sort,TreeMap的compare,以及Resource
public interface Expression{
/**
* 解释表达式的抽象方法
* @param map 比如现有表达式: a+b; 那么map中存放的就是{a = 10,b =20}
* @return 返回解释后的值
*/
int interpreter(Map<String,Integer> map);
}
public class VarExpression implements Expression{
// 公式中的变量
private String key;
public VarExpression(String key){
this.key = key;
}
// 通过key获取所对应的值
@Override
public int interpreter(Map<String,Integer> map){
return map.get(key);
}
}
public class SymbolExpression implements Expression{
/**
* 假如现有表达式:a+b-c需要解析
* 分析:
* 1、一个运算符连接的是它左右两个数字
* 2、如上表达式【+】号连接的是吧“a”和“b”,【-】号连接的是“a+b”和“c”。
* 3、经过分析我们将运算符连接的左右都看成是一个表达式也就是Expression.
*/
// 左表达式
protected Expression leftExpression;
// 右表达式
protected Expression rightExpression;
public SymbolExpression(Expression leftExpression,Expression rightExpression){
this.leftExpression = leftExpression;
this.rightExpression = rightExpression;
}
// 不同种类的运算符由不同的运算符子类进行解析,所以该类不实现interpreter方法
@Override
public int interpreter(Map<String,Integer> map){
return 0;
}
}
public class SubExpression extends SymboExpression{
public SubExpression(Expression leftExpression,Expression rightExpression){
super(leftExpression,rightExpression);
}
// 解释减法
@Override
public int interpreter(Map<String,Integer> map){
return leftExpression.interpreter(map) - rightExpression.interpreter(map);
}
}
public class AddExpression extends SymbolExpression{
public AddExpression(Expression leftExpression,Expression rightExpression){
super(leftExpression,rightExpression);
}
// 解释加法
@Override
public int interpreter(Map<String,Integer> map){
return leftExpression.interpreter(map) + rightExpression.interpreter(map);
}
}
public class Calculator{
// 表达式
private Expression expression;
public Calcuator(String strExpression){
char[] charArray = strExpression.toCharArray();
// 定义栈用于存储表达式,因示例简单故不考虑运算顺序。
Stack<Expression> stack = new Stack<>();
Expression left;
Expression right;
// 解析表达式
for(int i = 0; i < charArray.length; i++){
switch(charArray[i]){
case '+':
// 获取左表达式
left = stack.pop();
// 定义右表达式
right = new VarExpression(String.valueOf(charArray[++i]));
// 将合并为一个新的表达式并放入栈中
stack.push(new AddExpression(left,right));
break;
case '-':
// 过程跟加法一样
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new SubExpression(left,right));
break;
default:
// 不是运算符
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
// 遍历完成获取最终解析好的表达式
this.expression = stack.pop();
}
/**
* @param map 表达式对应的值
* @return 计算的结果
*/
public int calculate(Map<String,Integer> map){
return this.expression.interpreter(map);
}
}
public class Client{
public static void main(String[] args){
// 表达式
String strExpression = "a+b-c+d";
// 表达式对应的值
Map<String,Integer> map = new HashMap<>();
map.put("a",2);
map.put("b",10);
map.put("c",8);
map.put("d",8);
// 创建计算器
Calculator calculator = new Calculator(strExpression);
// 计算
int result = calculator.calculate(map);
System.out.println("表达式:"+ strExpression +"的计算结果为:"+ result);
}
}
在框架源码中,比如SPEL 表达式、Parse解析器等都用到了解释器模式,它是一个不是很常用的解析器,我们虽然可能不会经常去写解释器模式的代码,但是会经常接触和使用到通过此模式创建出来的一些工具类;
public abstract class Subject{
protected List<Observer> observers = new ArrayList<Observer>();
// 增加观察者方法
public void add(Observer observer){
observers.add(observer);
}
// 删除观察者方法
public void remove(Observer observer){
observers.remove(observer);
}
// 通知观察者方法
public abstract void notifyObserver(); 通知观察者方法
}
public class ConcreteSubject extends Subject{
pubic void notifyObserver(){
System.out.println("具体目标已发生改变...");
for(Object obs: observers){
(Observer)obs.response();
}
}
}
public interface Observer{
void response(); // 通过通知后做出的应对方法
}
public class ConcreteObserver1 implements Observer{
public void response(){
System.out.println("具体观察者1做出反应!");
}
}
public class ConcreateObserver2 implements Observer{
public void response(){
System.out.println("具体观察者2作出反应!");
}
}
public class Article{
private String title;
private String content;
private String imgs;
public Article(String title,String content, String imgs){
this.title = title;
this.content = content;
this.imgs = imgs;
}
// getter、setter ...
public ArticleMemento saveToMemento(){
ArticleMemento articleMemento = new ArticleMemento(this.title,this.content,this.imgs);
return articleMemento;
}
public void undoFromMemento(ArticleMemento articleMemento){
this.title = articleMemento.getTitle();
this.content = articleMemento.getContent();
this.imgs = articleMemento.getImgs();
}
}
public class ArticleMemento{
private String title;
private String content;
private String imgs;
public ArticleMemento(String title,String content, String imgs){
this.title = title;
this.content = content;
this.imgs = imgs;
}
public String getTitle(){return title;}
public String getContent(){return content;}
public String getImgs(){return imgs;}
这个快照类,可以根据实际来存储一些能够还原成原来对象的一些信息;故里面的变量和具体方法可以根据实际情况来
public class ArticleMementoManager{
private final Stack<ArticleMemento> ARTICLE_MEMENTO_STACK = new Stack<>();
public ArticleMemento getMemento(){
ArticleMemento articleMemento = ARTICLE_MEMENTO_STACK.pop
}
public void addMemento(ArticleMemento articleMemento){
ARTICLE_MEMENTO_STACK.push(articleMemento);
}
}
public class Test{
public static void main(String[] args){
ArticleMementoManager articleMementoManager = new ArticleManager();
Article article = new Article("如影随形的设计模式A","设计模式的具体内容","设计模式的图片");
// 将文章内容暂存,返回暂存对象
ArticleMemento articleMemento = article.saveToMemento();
// 获取暂存对象放入快照管理类
articleMementoManager.addMemento(articleMemento);
// 修改文章内容
article.setTitle("随影随行的设计模式B");
// 此刻我们想要回归到之前的状态,则使用快照管理类进行回退
articleMemento = articleMementoManager.getMemento();
}
}
备忘录模式可以将一些常用变量进行存储,然后通过管理类进行统一存储和还原;
在工作流、涉及到可以回退的如文本编辑等功能,依据实际业务场景或许可以通过备忘录模式优雅的解决问题;
说白了,就是将一系列的请求命令封装起来,不直接调用真正执行者的方法,这样比较好扩展;
public class Receiver{
public void action(){
System.out.println("命令执行了...");
}
}
public interface Command{
// 调用命令
void execute();
}
public class ConcreteCommand implements Command{
// 持有真正执行命令对象的引用
private Receiver receiver;
public ConcreteCommand(Receiver receiver){
super();
this.receiver = receiver;
}
@Override
public void execute(){
// 调用接收者执行命令的方法
receiver.action();
}
}
// 请求者/调用者:发起执行命令请求的对象
public class Invoker{
// 持有命令对象的引用
private Command command;
public Invoker(Command command){
super();
this.command = command;
}
public void call(){
// 请求者调用命令对象执行命令的那个execute方法
command.execute();
}
}
public class Test{
public static void main(String[] args){
// 通过请求者(invoker)调用命令对象(command),命令对象中调用了命令具体执行者(receiver)
Commmand command = new ConcreteCommand(new Receiver());
Invoker invoker = new Invoker(command);
invoker.call();
}
}
public class StudyGroup{
public static void showMessage(User user,String message){
System.out.println(new Date().toString() + "["+user.geName()+"]:"+ message );
}
}
public class User{
private String name;
public String getName(){return name;}
public void setName(String name){this.name = name;}
public User(String name){this.name = name;}
public void sendMessage(String message){
StudyGroup.showMessage(this,message);
}
}
public class Test{
public static void main(String[] args){
User geely = new User("Geely");
User tom = new User("Tom");
geely.sendMessage(" Hey ! Tom! Let's learn Design Pattern...");
tom.sendMesage("OK! Geely");
}
}
中介者模式适合做聊天室这种;
public class Course{
private String name;
private String article;
private String video;
// getter...setter... toString...
}
public abstract class Approver{
protected Approver approver;
public void setNextApprover(Approver approver){
this.approver = approver;
}
public abstract void deploy(Course course);
}
public class ArticleApprover extends Approver{
@Override
public void deploy(Course course){
if(StringUtils.isNotEmpty(course.getArticle())){
System.out.println(course.getName()+"含有手记,批准");
}
if(approver != null){
approver.deploy(course);
}
}else{
System.out.println(course.getName()+"不含有手记,不批准,流程结束");
return;
}
}
public class VedioApprover extends Approver{
@Override
public void deploy(Course course){
if(StringUtils.isNotEmpty(course.getVideo())){
System.out.println(course.getName()+"含有视频,批准");
}
if(approver != null){
approver.deploy(course);
}
}else{
System.out.println(course.getName()+"不含有视频,不批准,流程结束");
return;
}
}
public class Test{
public static void main(String[] args){
Approver articleApprover = new ArticleApprover();
Approver videoApprover = new VideoApprover();
Course course = new Course();
course.setName("Java 设计模式");
course.setArticle("Java 设计模式附带的文章");
course.setVideo("Java设计模式附带的视频");
articleApprover.setNextApprover(videoApprover);
articleApprover.deploy(course);
}
}
责任链模式在源码中的应用有:Filter 过滤器、Spring Security等;
业务场景介绍:
代码实现:
// 员工基类
public abstract class Staff {
public String name;
public int kpi; // 员工kpi
public Staff(String name){
this.name = name;
kpi = new Random().nextInt(10);
}
// 核心方法,接受Vistor的访问
public abstract void accept(Vistor vistor);
}
Staff 类定义了员工基本信息及一个 accept 方法,accept 方法表示接受访问者的访问,由子类具体实现。Visitor 是个接口,传入不同的实现类,可访问不同的数据。
// 工程师
public class Engineer extends Staff {
public Engineer(String name){
super(name);
}
@Override
public void accept(Visitor visitor){
visitor.visit(this);
}
// 工程师一年的代码数量
public int getCodeLines(){
return new Random().nextInt(10 * 100000);
}
}
public class Manager extends Staff {
public Manager(String name){
super(name);
}
@Override
public void accept(Visitor visitor){
visitor.visit(this);
}
// 一年做的产品数量
public int getProducts(){
return new Random().nextInt(10);
}
}
工程师是代码数量,经理是产品数量,他们的职责不一样,也就是因为差异性,才使得访问模式能够发挥它的作用。Staff、Engineer、Manager 3个类型就是对象结构,这些类型相对稳定,不会发生变化。
// 员工业务报表类
public class BusinessReport {
private List<Staff> mStaffs = new LinkedList<>();
public BusinessReport() {
mStaffs.add(new Manager("经理-A"));
mStaffs.add(new Engineer("工程师-A"));
mStaffs.add(new Engineer("工程师-B"));
mStaffs.add(new Engineer("工程师-C"));
mStaffs.add(new Manager("经理-B"));
mStaffs.add(new Engineer("工程师-D"));
}
/**
* 为访问者展示报表
* @param visitor 公司高层,如CEO、CTO
*/
public void showReport(Visitor visitor) {
for (Staff staff : mStaffs) {
staff.accept(visitor);
}
}
}
public interface Visitor{
// 访问工程师类型
void visit(Engineer engineer);
// 访问经理类型
void visit(Manager manager);
}
首先定义了一个 Visitor 接口,该接口有两个 visit 函数,参数分别是 Engineer、Manager,也就是说对于 Engineer、Manager 的访问会调用两个不同的方法,以此达成区别对待、差异化处理。
public class CEOVisitor implements Visitor {
@Override
public void visit(Engineer engineer){
System.out.println("工程师: " + engineer.name + ", KPI: " + engineer.kpi);
}
@Override
public void visit(Manager manager) {
System.out.println("经理: " + manager.name + ", KPI: " + manager.kpi +", 新产品数量: " + manager.getProducts());
}
}
public class CTOVisitor implements Visitor {
@Override
public void visit(Engineer engineer) {
System.out.println("工程师: " + engineer.name + ", 代码行数: " + engineer.getCodeLines());
}
@Override
public void visit(Manager manager) {
System.out.println("经理: " + manager.name + ", 产品数量: " + manager.getProducts());
}
}
重载的 visit 方法会对元素进行不同的操作,而通过注入不同的 Visitor 又可以替换掉访问者的具体实现,使得对元素的操作变得更灵活,可扩展性更高,同时也消除了类型转换、if-else 等“丑陋”的代码
public class Test{
public static void main(String[] args){
// 构建报表
BusinessReport report = new BusinessReport();
System.out.println("=====CEO 看报表======");
report.showReport(new CEOVisitor());
System.out.println("=====CTO 看报表======");
report.showReport(new CTOVisitor());
}
}
不使用Visitor模式,访问者代码如下:
public class ReportUtil {
public void visit(Staff staff) {
if (staff instanceof Manager) {
Manager manager = (Manager) staff;
System.out.println("经理: " + manager.name + ", KPI: " + manager.kpi +
", 新产品数量: " + manager.getProducts());
} else if (staff instanceof Engineer) {
Engineer engineer = (Engineer) staff;
System.out.println("工程师: " + engineer.name + ", KPI: " + engineer.kpi);
}
}
}
在CEO的访问者中,CEO关注工程师的 KPI,经理的 KPI 和新产品数量,通过两个 visitor 方法分别进行处理。如果不使用 Visitor 模式,只通过一个 visit 方法进行处理,那么就需要在这个 visit 方法中进行判断,然后分别处理,这就导致了 if-else 逻辑的嵌套以及类型的强制转换,难以扩展和维护,当类型较多时,这个 ReportUtil 就会很复杂。而使用 Visitor 模式,通过同一个函数对不同对元素类型进行相应对处理,使结构更加清晰、灵活性更高。
在上述示例中,Staff 扮演了 Element 角色,而 Engineer 和 Manager 都是 ConcreteElement;CEOVisitor 和 CTOVisitor 都是具体的 Visitor 对象;而 BusinessReport 就是 ObjectStructure;Client就是客户端代码。
访问者模式最大的优点就是增加访问者非常容易,我们从代码中可以看到,如果要增加一个访问者,只要新实现一个 Visitor 接口的类,从而达到数据对象与数据操作相分离的效果。如果不实用访问者模式,而又不想对不同的元素进行不同的操作,那么必定需要使用 if-else 和类型转换,这使得代码难以升级维护。
public abstract class CourseVideoState {
protected CourseVideoContext courseVideoContext;
public void setCourseVideoContext(CourseVideoContext courseVideoContext){
this.courseVideoContext = courseVideoContext;
}
public abstract void play();
public abstract void speed();
public abstract void pause();
pulbic abstract void stop();
}
public class CourseVideoContext{
private CourseVideoState courseVideoState;
public final static PlayState PLAY_STATE = new PlayState();
public final static StopState STOP_STATE = new StopState();
public final static PauseState PAUSE_STATE = new PauseState();
public final static SpeedState SPEED_STATE = new SpeedSate();
public CourseVideoSate getCourseVideoState(){
return courseVideoState();
}
public void setCourseVideoState(CourseVideoState courseVideoState){
this.courseVideoState = courseVideoState;
this.courseVideoState.setCourseVideoContext(this);
}
public void play(){
this.courseVideoState.play();
}
public void speed(){
this.courseVideoState.speed();
}
public void stop(){
this.courseVideoState.stop();
}
public void pause(){
this.courseVideoState.pause();
}
}
public class PlayState extends CourseVideoState{
@Override
public void play(){
System.out.println("正常播放课程视频状态");
}
@Override
public void speed(){
super.courseVideoContext.setCourseVideoState(CourseVideo.SPEED_STATE);
}
@Override
public void stop(){
super.courseVideoContext.setCourseVideoState(CourseVideo.STOP_STATE);
}
@Override
public void pause(){
super.courseVideoContext.setCourseVideoState(CourseVideo.PAUSE_STATE);
}
}
public class SpeedState extends CourseVideoState{
@Override
public void play(){
super.courseVideoContext.setCourseVideoState(CourseVideo.PLAY_STATE);
}
@Override
public void speed(){
System.out.println("快进播放课程视频状态");
}
@Override
public void stop(){
super.courseVideoContext.setCourseVideoState(CourseVideo.STOP_STATE);
}
@Override
public void pause(){
super.courseVideoContext.setCourseVideoState(CourseVideo.PAUSE_STATE);
}
}
public class StopState extends CourseVideoState{
@Override
public void play(){
super.courseVideoContext.setCourseVideoState(CourseVideo.PLAY_STATE);
}
@Override
public void speed(){
super.courseVideoContext.setCourseVideoState(CourseVideo.SPEED_STATE);
}
@Override
public void stop(){
super.courseVideoContext.setCourseVideoState(CourseVideo.STOP_STATE);
}
@Override
public void pause(){
System.out.println("暂停播放课程视频状态");
}
}
public class StopState extends CourseVideoState{
@Override
public void play(){
super.courseVideoContext.setCourseVideoState(CourseVideo.PLAY_STATE);
}
@Override
public void speed(){
System.out.println("ERROR 停止状态不能快进");
}
@Override
public void stop(){
System.out.println("停止播放课程视频状态");
}
@Override
public void pause(){
System.out.println("ERROR 停止状态不能暂停");
}
}
public class Test{
public static void main(String[] args){
CourseVideoContext courseVideoContext = new CourseVideoContext();
courseVideoContext.setCourseVideoState(new PlayState());
System.out.println("当前状态"+ courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.pause();
System.out.println("当前状态"+ courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.speed();
System.out.println("当前状态"+ courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.stop();
System.out.println("当前状态"+ courseVideoContext.getCourseVideoState().getClass().getSimpleName());
}
}
黄色部分可能会经常用到
此篇博客码字9万余字,码字不易,看到这儿的您也不易,不要忘了收藏哦~~