前言
“访问者模式是要解决对对象添加新的操作和功能时候,如何尽可能不修改对象的类的一种方法。”我觉得这个解释非常好,我们使用观察者模式的用意就是完成这个目的,以至于添加一个统一的接口accept,来接受一个访问者对象,怎么做到区别对待呢,那就是通过Visitor的不同来处理不同的操作,再怎么解释也不如甩个demo。
适用模式
要用这个模式,其对象必须要结构稳定,当然子类可以增加属性;另一方面如果使用对象不一样执行结果也不相同,那么这个模式很实用。
角色扮演
Visitor:接口或者抽象类,定义一个元素的访问行为,它的参数是可访问的对象,对于访问者不同可以有不同的处理方法;但要求元素要稳定,如果很容易导致这个添加、删除元素,那么不适合这个模式。
ConcreteVisitor:具体的访问者,处理具体行为。
Element:元素接口或者抽象类。定义一个接受访问者的方法,其意义是每一个元素都可以呗访问。
ConcreteElement:具体元素类,提供接受访问方法的具体实现。
ObjectStructure:定义所提到的对象结构,其内部管理元素集合。
demo
抽象一个简单的模型,男生女生对于买手机的要求不太一样,所以对手机的评分也不一样,模拟一个男女去手机店买手机的过程,抽象为双方对手机的评分。
首先我们来定义观察者:
抽象类:
package com.demo.visitor;
/**
* Created by italkbb on 2017/12/26.
*/
public interface Visitor {
void visit(IPhone iPhone);
void visit(AndroidPhone androidPhone);
}
其中用到了Phone的实体,我们先列出来:
package com.demo.visitor;
import java.util.Random;
/**
* Created by italkbb on 2017/12/26.
*/
public abstract class AbsPhone {
public String modelName;
public int money;
public AbsPhone(String modelName){
this.modelName = modelName;
this.money = new Random().nextInt(2000);
}
// 定义一个访问方法
public abstract void accept(Visitor visitor);
}
这个Abs节后相当于Element角色,那么我们可以实现安卓手机和iphone手机:
package com.demo.visitor;
import java.util.Random;
/**
* Created by italkbb on 2017/12/26.
*/
public class AndroidPhone extends AbsPhone {
public AndroidPhone(String modelName) {
super(modelName);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
/**
*
* 获取手机颜值等级
* @return
*/
public int getFaceScore(){
return new Random().nextInt(5);
}
/**
* 性能分数
* @return
*/
public int getPerformance(){
return new Random().nextInt(5);
}
}
package com.demo.visitor;
import java.util.Random;
/**
* Created by italkbb on 2017/12/26.
*/
public class IPhone extends AbsPhone {
public IPhone(String modelName) {
super(modelName);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
/**
* 性能分数
* @return
*/
public int getPerformance(){
return new Random().nextInt(5);
}
}
这都实现了之后,我们要实现Visitor的具体实现:
package com.demo.visitor;
import android.util.Log;
/**
* Created by italkbb on 2017/12/26.
*/
public class MaleConsumer implements Visitor {
public final static String CLASSNAME = MaleConsumer.class.getSimpleName();
@Override
public void visit(IPhone iPhone) {
double score = 0;
if (iPhone.money > 5000){
score = 3 * 0.6;
}else{
score = 5 * 0.6;
}
score = score + iPhone.getPerformance() * 0.4;
Log.v(CLASSNAME,iPhone.modelName + "在男生心里得分:" + score);
}
@Override
public void visit(AndroidPhone androidPhone) {
double score = 0;
if (androidPhone.money > 2000){
score = 3 * 0.3;
}else{
score = 5 * 0.3;
}
score = score + androidPhone.getPerformance() * 0.7;
Log.v(CLASSNAME,androidPhone.modelName + "在男生心里得分:" + score);
}
}
package com.demo.visitor;
import android.util.Log;
/**
* Created by italkbb on 2017/12/26.
*/
public class FamaleConsumer implements Visitor {
public final static String CLASSNAME = FamaleConsumer.class.getSimpleName();
@Override
public void visit(IPhone iPhone) {
double score = 0;
if (iPhone.money > 5000){
score = 3;
}else{
score = 5;
}
Log.v(CLASSNAME,iPhone.modelName + "在女生心里得分:" + score);
}
@Override
public void visit(AndroidPhone androidPhone) {
double score = 0;
if (androidPhone.money > 2000){
score = 3 * 0.2;
}else{
score = 5 * 0.2;
}
score = score + androidPhone.getFaceScore() * 0.8;
Log.v(CLASSNAME,androidPhone.modelName + "在女生心里得分:" + score);
}
}
现在我们是有了Visitor和Element类以及实现,我们迫切的需求手机店,不然没啥用:
package com.demo.visitor;
import java.util.ArrayList;
import java.util.List;
/**
* Created by italkbb on 2017/12/26.
*/
public class PhoneMarket {
List<AbsPhone> mPhones = new ArrayList<>();
public PhoneMarket(){
// 列举市场里面的手机
mPhones.add(new AndroidPhone("OnePlus 5T"));
mPhones.add(new IPhone("iphone6s"));
mPhones.add(new AndroidPhone("Huawei P10"));
mPhones.add(new IPhone("iphone7"));
mPhones.add(new AndroidPhone("Pixel XL"));
mPhones.add(new IPhone("iphoneX"));
mPhones.add(new AndroidPhone("Mix2"));
}
/**
* 根据考察者来决定手机得分
* @param visitor
*/
public void showPhoneLevel(Visitor visitor){
for (AbsPhone phone:mPhones){
phone.accept(visitor);
}
}
}
写完这些类,我们只差使用了,Client为使用:
package com.demo.visitor;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import teltplay.example.com.kotlindemo.R;
public class VisitorActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_visitor);
PhoneMarket phoneMarket = new PhoneMarket();
// 男生看手机
phoneMarket.showPhoneLevel(new MaleConsumer());
// 女生看手机
phoneMarket.showPhoneLevel(new FamaleConsumer());
}
}
简单的实现了这个Demo,我想这样的话简单的理解访问者模式就简单了。
后记
访问者模式符合单一职责原则,易于扩展, 对于结构稳定的数据使用该模式是个不二之选,这样一说的话缺点也就暴露出来了,当具体元素变更时将导致修改的成本变大。