在之前的文章我们已经介绍了设计模式中的创建者模式和结构型模式,下面我们来介绍最后一部分行为型模式
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务
行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
下面我们将介绍十一种行为型模式:
首先我们来介绍模板方法模式
首先我们给出模板方法模式的概念:
模板方法(Template Method)模式包含以下主要角色:
抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:
抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。
具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。
具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。
我们给出一个简单的例子来介绍模板方法模式:

具体分析:
/*
【例】炒菜
炒菜的步骤是固定的,分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。现通过模板方法模式来用代码模拟。
上述的AbstractClass就是抽象类,我们在抽象类给出一个模板方法cookProcess,里面会给出其他基本方法的执行顺序,部分基本方法会有具体内容,部分基本方法属于Abstract方法,由子类去实现
下面的ConcreteClass_BaoCai和ConcreteClass_CaiXin属于子类实现类,他们会继承父类的模板方法,同时重写抽象基本方法完成自己的需求
*/
/* 具体代码 */
// 抽象类
public abstract class AbstractClass {
// 模板方法(为防止恶意操作,一般模板方法都加上 final 关键词)
public final void cookProcess() {
//第一步:倒油
this.pourOil();
//第二步:热油
this.heatOil();
//第三步:倒蔬菜
this.pourVegetable();
//第四步:倒调味料
this.pourSauce();
//第五步:翻炒
this.fry();
}
// 下述均为基本方法
public void pourOil() {
System.out.println("倒油");
}
//第二步:热油是一样的,所以直接实现
public void heatOil() {
System.out.println("热油");
}
//第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
public abstract void pourVegetable();
//第四步:倒调味料是不一样
public abstract void pourSauce();
//第五步:翻炒是一样的,所以直接实现
public void fry(){
System.out.println("炒啊炒啊炒到熟啊");
}
}
// Baocai实现类
public class ConcreteClass_BaoCai extends AbstractClass {
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是包菜");
}
@Override
public void pourSauce() {
System.out.println("下锅的酱料是辣椒");
}
}
// Caixin实现类
public class ConcreteClass_CaiXin extends AbstractClass {
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是菜心");
}
@Override
public void pourSauce() {
System.out.println("下锅的酱料是蒜蓉");
}
}
public class Client {
public static void main(String[] args) {
//炒手撕包菜
ConcreteClass_BaoCai baoCai = new ConcreteClass_BaoCai();
baoCai.cookProcess();
//炒蒜蓉菜心
ConcreteClass_CaiXin caiXin = new ConcreteClass_CaiXin();
caiXin.cookProcess();
}
}
首先我们给出模板方法模式的适用场景:
然后我们给出模板方法模式的优点:
提高代码复用性
将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中。
实现了反向控制
通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制 ,并符合“开闭原则”。
最后我们给出模板方法模式的缺点:
下面我们来介绍策略模式
首先我们给出策略模式的概念:
策略模式和模板模式其实比较相似,只不过前者使用聚合,后者使用继承。
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。
对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
我们给出一个简单的例子说明:
策略模式的主要角色如下:
我们同样给出一个简单的案例讲解策略模式:

具体分析:
/*
【例】促销活动
一家百货公司在定年度的促销活动。针对不同的节日(春节、中秋节、圣诞节)推出不同的促销活动,由促销员将促销活动展示给客户。
其中SalesMan就是环境类,Strategy是抽象策略类,下面的方案就是具体策略类
SalesMan中聚合一个Strategy,然后会用子类去填充,具有一定格式但不同实现的子类就可以不断更替Strategy而实现策略更换
*/
/* 代码展示 */
// 环境类(调用更换策略类)
public class SalesMan {
//持有抽象策略角色的引用
private Strategy strategy;
public SalesMan(Strategy strategy) {
this.strategy = strategy;
}
//向客户展示促销活动
public void salesManShow(){
strategy.show();
}
}
// 抽象策略类
public interface Strategy {
void show();
}
// 具体策略类
//为春节准备的促销活动A
public class StrategyA implements Strategy {
public void show() {
System.out.println("买一送一");
}
}
//为中秋准备的促销活动B
public class StrategyB implements Strategy {
public void show() {
System.out.println("满200元减50元");
}
}
//为圣诞准备的促销活动C
public class StrategyC implements Strategy {
public void show() {
System.out.println("满1000元加一元换购任意200元以下商品");
}
}
首先我们给出策略模式的适用场景:
然后我们给出策略模式的优点:
策略类之间可以自由切换
由于策略类都实现同一个接口,所以使它们之间可以自由切换。
易于扩展
增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合“开闭原则“
避免使用多重条件选择语句(if else),充分体现面向对象设计思想。
最后我们给出策略模式的缺点:
下面我们来介绍命令模式
首先我们给出命令模式的概念:
我们给出一个简单示例:
命令模式包含以下主要角色:
我们给出一个简单的案例来介绍命令模式:

具体分析:
/*
将上面的案例用代码实现,那我们就需要分析命令模式的角色在该案例中由谁来充当。
服务员: 就是调用者角色,由她来发起命令。
资深大厨: 就是接收者角色,真正命令执行的对象。
订单: 命令中包含订单。
注意:
订单就是命令,命令是自己具有的,有指定的接收对象
服务员只负责将启动命令,实则还是调用命令内部的方法
*/
/* 代码展示 */
// 抽象命令类
public interface Command {
void execute();//只需要定义一个统一的执行方法
}
// 具体命令类
public class OrderCommand implements Command {
// 持有接受者对象(就是实现者,当调用者调用命令后实现者会去执行该命令)
private SeniorChef receiver;
// 执行的内容存储
private Order order;
public OrderCommand(SeniorChef receiver, Order order){
this.receiver = receiver;
this.order = order;
}
// 具体的命令执行,调用者只负责调用该方法
public void execute() {
System.out.println(order.getDiningTable() + "桌的订单:");
Set<String> keys = order.getFoodDic().keySet();
for (String key : keys) {
receiver.makeFood(order.getFoodDic().get(key),key);
}
try {
Thread.sleep(100);//停顿一下 模拟做饭的过程
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(order.getDiningTable() + "桌的饭弄好了");
}
}
// domain实体类
public class Order {
// 餐桌号码
private int diningTable;
// 用来存储餐名并记录份数
private Map<String, Integer> foodDic = new HashMap<String, Integer>();
public int getDiningTable() {
return diningTable;
}
public void setDiningTable(int diningTable) {
this.diningTable = diningTable;
}
public Map<String, Integer> getFoodDic() {
return foodDic;
}
public void setFoodDic(String name, int num) {
foodDic.put(name,num);
}
}
// 资深大厨类 是命令的Receiver
public class SeniorChef {
public void makeFood(int num,String foodName) {
System.out.println(num + "份" + foodName);
}
}
// 调用者,负责协调请求和接收者
public class Waitor {
private ArrayList<Command> commands;//可以持有很多的命令对象
public Waitor() {
commands = new ArrayList();
}
public void setCommand(Command cmd){
commands.add(cmd);
}
// 发出命令 喊 订单来了,厨师开始执行
public void orderUp() {
System.out.println("美女服务员:叮咚,大厨,新订单来了.......");
for (int i = 0; i < commands.size(); i++) {
Command cmd = commands.get(i);
if (cmd != null) {
cmd.execute();
}
}
}
}
// 测试类
public class Client {
public static void main(String[] args) {
//创建2个order
Order order1 = new Order();
order1.setDiningTable(1);
order1.getFoodDic().put("西红柿鸡蛋面",1);
order1.getFoodDic().put("小杯可乐",2);
Order order2 = new Order();
order2.setDiningTable(3);
order2.getFoodDic().put("尖椒肉丝盖饭",1);
order2.getFoodDic().put("小杯雪碧",1);
//创建接收者
SeniorChef receiver=new SeniorChef();
//将订单和接收者封装成命令对象
OrderCommand cmd1 = new OrderCommand(receiver, order1);
OrderCommand cmd2 = new OrderCommand(receiver, order2);
//创建调用者 waitor
Waitor invoker = new Waitor();
invoker.setCommand(cmd1);
invoker.setCommand(cmd2);
//将订单带到柜台 并向厨师喊 订单来了
invoker.orderUp();
}
}
我们首先给出命令模式的适用场景:
然后我们给出命令模式的优点:
最后我们给出命令模式的缺点:
下面我们来介绍责任链模式
首先我们先来简单介绍一下责任链模式:
我们给出一个简单例子:
职责链模式主要包含以下角色:
我们同样给出一个简单案例来讲解责任链模式:

具体分析:
/*
【例】
现需要开发一个请假流程控制系统。
请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部门经理同意;请求3天到7天还需要总经理同意才行。
LeaveRequest:请假条,记录任命,日期,信息;属于实体类
Handler:抽象处理者
Leader:具体处理者
*/
/* 代码展示 */
// 请假条(实体类,仅用于记录信息)
public class LeaveRequest {
private String name;//姓名
private int num;//请假天数
private String content;//请假内容
public LeaveRequest(String name, int num, String content) {
this.name = name;
this.num = num;
this.content = content;
}
public String getName() {
return name;
}
public int getNum() {
return num;
}
public String getContent() {
return content;
}
}
// 处理者抽象类
public abstract class Handler {
// 请假日期分界线,用于子类使用
protected final static int NUM_ONE = 1;
protected final static int NUM_THREE = 3;
protected final static int NUM_SEVEN = 7;
// 该领导处理的请假天数区间
private int numStart;
private int numEnd;
// 领导上面还有领导(责任链的下一位)
private Handler nextHandler;
// 设置请假天数范围 上不封顶
public Handler(int numStart) {
this.numStart = numStart;
}
// 设置请假天数范围
public Handler(int numStart, int numEnd) {
this.numStart = numStart;
this.numEnd = numEnd;
}
// 设置上级领导
public void setNextHandler(Handler nextHandler){
this.nextHandler = nextHandler;
}
// 提交请假条(这里是一个提交方法,参数为leaveRequest,主要是给子类的领导层使用的)
public final void submit(LeaveRequest leave){
// 首先判断是否请假
if(0 == this.numStart){
return;
}
//如果请假天数达到该领导者的处理要求
if(leave.getNum() >= this.numStart){
this.handleLeave(leave);
//如果还有上级 并且请假天数超过了当前领导的处理范围
if(null != this.nextHandler && leave.getNum() > numEnd){
this.nextHandler.submit(leave);//继续提交
} else {
System.out.println("流程结束");
}
}
}
// 各级领导处理请假条方法
protected abstract void handleLeave(LeaveRequest leave);
}
// 小组长
public class GroupLeader extends Handler {
public GroupLeader() {
//小组长处理1-3天的请假
super(Handler.NUM_ONE, Handler.NUM_THREE);
}
@Override
protected void handleLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
System.out.println("小组长审批:同意。");
}
}
// 部门经理
public class Manager extends Handler {
public Manager() {
//部门经理处理3-7天的请假
super(Handler.NUM_THREE, Handler.NUM_SEVEN);
}
@Override
protected void handleLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
System.out.println("部门经理审批:同意。");
}
}
// 总经理
public class GeneralManager extends Handler {
public GeneralManager() {
//部门经理处理7天以上的请假
super(Handler.NUM_SEVEN);
}
@Override
protected void handleLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "。");
System.out.println("总经理审批:同意。");
}
}
// 测试类
public class Client {
public static void main(String[] args) {
//请假条来一张
LeaveRequest leave = new LeaveRequest("小花",5,"身体不适");
//各位领导
GroupLeader groupLeader = new GroupLeader();
Manager manager = new Manager();
GeneralManager generalManager = new GeneralManager();
groupLeader.setNextHandler(manager);//小组长的领导是部门经理
manager.setNextHandler(generalManager);//部门经理的领导是总经理
//之所以在这里设置上级领导,是因为可以根据实际需求来更改设置,如果实战中上级领导人都是固定的,则可以移到领导实现类中。
//提交申请
groupLeader.submit(leave);
}
}
首先我们给出责任链模式的优点:
降低了对象之间的耦合度
该模式降低了请求发送者和接收者的耦合度。
增强了系统的可扩展性
可以根据需要增加新的请求处理类,满足开闭原则。
增强了给对象指派职责的灵活性
当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。
责任链简化了对象之间的连接
一个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
责任分担
每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
然后我们给出责任链模式的缺点:
下面我们来介绍状态模式
首先我们给出状态模式的概念:
状态模式包含以下主要角色。
我们首先给出一个非状态模式:

具体分析:
/*
【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作。
下述代码问题:
- 使用了大量的switch…case这样的判断(if…else也是一样),使程序的可阅读性变差。
- 扩展性很差。如果新加了断电的状态,我们需要修改上面判断逻辑
*/
/* 代码展示 */
public interface ILift {
//电梯的4个状态
//开门状态
public final static int OPENING_STATE = 1;
//关门状态
public final static int CLOSING_STATE = 2;
//运行状态
public final static int RUNNING_STATE = 3;
//停止状态
public final static int STOPPING_STATE = 4;
//设置电梯的状态
public void setState(int state);
//电梯的动作
public void open();
public void close();
public void run();
public void stop();
}
public class Lift implements ILift {
private int state;
@Override
public void setState(int state) {
this.state = state;
}
//执行关门动作
@Override
public void close() {
switch (this.state) {
case OPENING_STATE:
System.out.println("电梯关门了。。。");//只有开门状态可以关闭电梯门,可以对应电梯状态表来看
this.setState(CLOSING_STATE);//关门之后电梯就是关闭状态了
break;
case CLOSING_STATE:
//do nothing //已经是关门状态,不能关门
break;
case RUNNING_STATE:
//do nothing //运行时电梯门是关着的,不能关门
break;
case STOPPING_STATE:
//do nothing //停止时电梯也是关着的,不能关门
break;
}
}
//执行开门动作
@Override
public void open() {
switch (this.state) {
case OPENING_STATE://门已经开了,不能再开门了
//do nothing
break;
case CLOSING_STATE://关门状态,门打开:
System.out.println("电梯门打开了。。。");
this.setState(OPENING_STATE);
break;
case RUNNING_STATE:
//do nothing 运行时电梯不能开门
break;
case STOPPING_STATE:
System.out.println("电梯门开了。。。");//电梯停了,可以开门了
this.setState(OPENING_STATE);
break;
}
}
//执行运行动作
@Override
public void run() {
switch (this.state) {
case OPENING_STATE://电梯不能开着门就走
//do nothing
break;
case CLOSING_STATE://门关了,可以运行了
System.out.println("电梯开始运行了。。。");
this.setState(RUNNING_STATE);//现在是运行状态
break;
case RUNNING_STATE:
//do nothing 已经是运行状态了
break;
case STOPPING_STATE:
System.out.println("电梯开始运行了。。。");
this.setState(RUNNING_STATE);
break;
}
}
//执行停止动作
@Override
public void stop() {
switch (this.state) {
case OPENING_STATE: //开门的电梯已经是是停止的了(正常情况下)
//do nothing
break;
case CLOSING_STATE://关门时才可以停止
System.out.println("电梯停止了。。。");
this.setState(STOPPING_STATE);
break;
case RUNNING_STATE://运行时当然可以停止了
System.out.println("电梯停止了。。。");
this.setState(STOPPING_STATE);
break;
case STOPPING_STATE:
//do nothing
break;
}
}
}
public class Client {
public static void main(String[] args) {
Lift lift = new Lift();
lift.setState(ILift.STOPPING_STATE);//电梯是停止的
lift.open();//开门
lift.close();//关门
lift.run();//运行
lift.stop();//停止
}
}
然后我们给出状态模式下的修改案例:

具体分析:
/*
对上述电梯的案例使用状态模式进行改进
*/
/* 代码展示 */
//抽象状态类
public abstract class LiftState {
//定义一个环境角色,也就是封装状态的变化引起的功能变化
protected Context context;
public void setContext(Context context) {
this.context = context;
}
//电梯开门动作
public abstract void open();
//电梯关门动作
public abstract void close();
//电梯运行动作
public abstract void run();
//电梯停止动作
public abstract void stop();
}
//开启状态
public class OpenningState extends LiftState {
//开启当然可以关闭了,我就想测试一下电梯门开关功能
@Override
public void open() {
System.out.println("电梯门开启...");
}
@Override
public void close() {
//状态修改
super.context.setLiftState(Context.closeingState);
//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
super.context.getLiftState().close();
}
//电梯门不能开着就跑,这里什么也不做
@Override
public void run() {
//do nothing
}
//开门状态已经是停止的了
@Override
public void stop() {
//do nothing
}
}
//运行状态
public class RunningState extends LiftState {
//运行的时候开电梯门?你疯了!电梯不会给你开的
@Override
public void open() {
//do nothing
}
//电梯门关闭?这是肯定了
@Override
public void close() {//虽然可以关门,但这个动作不归我执行
//do nothing
}
//这是在运行状态下要实现的方法
@Override
public void run() {
System.out.println("电梯正在运行...");
}
//这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了
@Override
public void stop() {
super.context.setLiftState(Context.stoppingState);
super.context.stop();
}
}
//停止状态
public class StoppingState extends LiftState {
//停止状态,开门,那是要的!
@Override
public void open() {
//状态修改
super.context.setLiftState(Context.openningState);
//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
super.context.getLiftState().open();
}
@Override
public void close() {//虽然可以关门,但这个动作不归我执行
//状态修改
super.context.setLiftState(Context.closeingState);
//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
super.context.getLiftState().close();
}
//停止状态再跑起来,正常的很
@Override
public void run() {
//状态修改
super.context.setLiftState(Context.runningState);
//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
super.context.getLiftState().run();
}
//停止状态是怎么发生的呢?当然是停止方法执行了
@Override
public void stop() {
System.out.println("电梯停止了...");
}
}
//关闭状态
public class ClosingState extends LiftState {
@Override
//电梯门关闭,这是关闭状态要实现的动作
public void close() {
System.out.println("电梯门关闭...");
}
//电梯门关了再打开,逗你玩呢,那这个允许呀
@Override
public void open() {
super.context.setLiftState(Context.openningState);
super.context.open();
}
//电梯门关了就跑,这是再正常不过了
@Override
public void run() {
super.context.setLiftState(Context.runningState);
super.context.run();
}
//电梯门关着,我就不按楼层
@Override
public void stop() {
super.context.setLiftState(Context.stoppingState);
super.context.stop();
}
}
//环境角色
public class Context {
//定义出所有的电梯状态
public final static OpenningState openningState = new OpenningState();//开门状态,这时候电梯只能关闭
public final static ClosingState closeingState = new ClosingState();//关闭状态,这时候电梯可以运行、停止和开门
public final static RunningState runningState = new RunningState();//运行状态,这时候电梯只能停止
public final static StoppingState stoppingState = new StoppingState();//停止状态,这时候电梯可以开门、运行
//定义一个当前电梯状态
private LiftState liftState;
public LiftState getLiftState() {
return this.liftState;
}
public void setLiftState(LiftState liftState) {
//当前环境改变
this.liftState = liftState;
//把当前的环境通知到各个实现类中
this.liftState.setContext(this);
}
public void open() {
this.liftState.open();
}
public void close() {
this.liftState.close();
}
public void run() {
this.liftState.run();
}
public void stop() {
this.liftState.stop();
}
}
//测试类
public class Client {
public static void main(String[] args) {
Context context = new Context();
context.setLiftState(new ClosingState());
context.open();
context.close();
context.run();
context.stop();
}
}
我们首先给出状态模式的适用场景:
然后我们给出状态模式的优点:
最后我们给出状态模式的缺点:
下面我们来介绍观察者模式
首先我们给出观察者模式的概念:
在观察者模式中有如下角色:
我们通过一个案例来解释观察者模式:

具体分析:
/*
【例】微信公众号
在使用微信公众号时,大家都会有这样的体验,当你关注的公众号中有新内容更新的话,它就会推送给关注公众号的微信用户端。
我们使用观察者模式来模拟这样的场景,微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了这个公众号。
其中微信公众号就是被观察者,被观察者可以存储多个观察者,当被观察者做出一些修改后,就会调用一个方法去通知观察者并修改内容
*/
/* 代码展示 */
// 抽象观察者(观察者中具有一个修改方法,当被观察者被修改后,会导致观察者调用update方法)
public interface Observer {
void update(String message);
}
// 具体观察者(这里是指用户,这里指wx公众号发表文章后,观察者会收到一条提示信息)
public class WeixinUser implements Observer {
// 微信用户名
private String name;
public WeixinUser(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + "-" + message);
}
}
// 抽象主题类(被观察者)
public interface Subject {
// 增加订阅者
public void attach(Observer observer);
// 删除订阅者
public void detach(Observer observer);
// 通知订阅者更新消息
public void notify(String message);
}
// 具体主题类(被观察者)
public class SubscriptionSubject implements Subject {
// 储存订阅公众号的微信用户
private List<Observer> weixinUserlist = new ArrayList<Observer>();
@Override
public void attach(Observer observer) {
weixinUserlist.add(observer);
}
@Override
public void detach(Observer observer) {
weixinUserlist.remove(observer);
}
// 当wx公众号发表文章,就会调用该方法,然后通知各个观察者去update
@Override
public void notify(String message) {
for (Observer observer : weixinUserlist) {
observer.update(message);
}
}
}
// 客户端信息
public class Client {
public static void main(String[] args) {
SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
//创建微信用户
WeixinUser user1=new WeixinUser("孙悟空");
WeixinUser user2=new WeixinUser("猪悟能");
WeixinUser user3=new WeixinUser("沙悟净");
//订阅公众号
mSubscriptionSubject.attach(user1);
mSubscriptionSubject.attach(user2);
mSubscriptionSubject.attach(user3);
//公众号更新发出消息给订阅的微信用户
mSubscriptionSubject.notify("传智黑马的专栏更新了");
}
}
首先我们给出观察者模式的适用场景:
然后我们给出观察者模式的优点:
最后我们给出观察者模式的缺点:
下面我们来介绍中介者模式
首先我们给出中介者模式的概念:
我们给出一个简单的示例:
中介者模式包含以下主要角色:
抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
具体中介者(ConcreteMediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。
我们通过一个简单的案例来介绍中介者模式:

具体分析:
/*
【例】租房
现在租房基本都是通过房屋中介,房主将房屋托管给房屋中介,而租房者从房屋中介获取房屋信息。房屋中介充当租房者与房屋所有者之间的中介者。
租房者本身需要和所有房东联系才能获得房屋信息,但是可以通过中介获得所有房屋信息,这就实现了解耦操作
*/
/* 代码展示 */
// 抽象中介者
public abstract class Mediator {
//申明一个联络方法(前者是信息,后者是信息发送人)
public abstract void constact(String message,Person person);
}
// 抽象同事类
public abstract class Person {
protected String name;
protected Mediator mediator;
public Person(String name,Mediator mediator){
this.name = name;
this.mediator = mediator;
}
}
// 具体同事类 房屋拥有者
public class HouseOwner extends Person {
public HouseOwner(String name, Mediator mediator) {
super(name, mediator);
}
//与中介者联系
public void constact(String message){
mediator.constact(message, this);
}
//获取信息
public void getMessage(String message){
System.out.println("房主" + name +"获取到的信息:" + message);
}
}
// 具体同事类 承租人
public class Tenant extends Person {
public Tenant(String name, Mediator mediator) {
super(name, mediator);
}
//与中介者联系
public void constact(String message){
mediator.constact(message, this);
}
//获取信息
public void getMessage(String message){
System.out.println("租房者" + name +"获取到的信息:" + message);
}
}
//中介机构
public class MediatorStructure extends Mediator {
//首先中介结构必须知道所有房主和租房者的信息
private HouseOwner houseOwner;
private Tenant tenant;
public HouseOwner getHouseOwner() {
return houseOwner;
}
public void setHouseOwner(HouseOwner houseOwner) {
this.houseOwner = houseOwner;
}
public Tenant getTenant() {
return tenant;
}
public void setTenant(Tenant tenant) {
this.tenant = tenant;
}
public void constact(String message, Person person) {
// 如果是房主进行联系,则对应的租房者获得房主的信息
if (person == houseOwner) {
tenant.getMessage(message);
} else { //反之则是房主获得信息
houseOwner.getMessage(message);
}
}
}
//测试类
public class Client {
public static void main(String[] args) {
//一个房主、一个租房者、一个中介机构
MediatorStructure mediator = new MediatorStructure();
//房主和租房者只需要知道中介机构即可
HouseOwner houseOwner = new HouseOwner("张三", mediator);
Tenant tenant = new Tenant("李四", mediator);
//中介结构要知道房主和租房者
mediator.setHouseOwner(houseOwner);
mediator.setTenant(tenant);
tenant.constact("需要租三室的房子");
houseOwner.constact("我这有三室的房子,你需要租吗?");
}
}
首先我们给出中介者模式的适用场景:
然后我们给出中介者模式的优点:
松散耦合
中介者模式通过把多个同事对象之间的交互封装到中介者对象里面,从而使得同事对象之间松散耦合,基本上可以做到互补依赖。这样一来,同事对象就可以独立地变化和复用,而不再像以前那样“牵一处而动全身”了。
集中控制交互
多个同事对象的交互,被封装在中介者对象里面集中管理,使得这些交互行为发生变化的时候,只需要修改中介者对象就可以了,当然如果是已经做好的系统,那么就扩展中介者对象,而各个同事类不需要做修改。
一对多关联转变为一对一的关联
没有使用中介者模式的时候,同事对象之间的关系通常是一对多的,引入中介者对象以后,中介者对象和同事对象的关系通常变成双向的一对一,这会让对象的关系更容易理解和实现。
最后我们给出中介者模式的缺点:
下面我们来介绍迭代器模式
首先我们来简单介绍一下迭代器模式:
迭代器模式主要包含以下角色:
抽象聚合(Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、next() 等方法。
具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
我们通过一个简单的案例来介绍迭代器:

具体分析:
/*
【例】定义一个可以存储学生对象的容器对象,将遍历该容器的功能交由迭代器实现
Student:学生实体类
StudentIterator:抽象迭代器,声明hasNext、next方法
StudentIteratorImpl:具体迭代器,重写所有的抽象方法
StudentAggregate:抽象容器类,包含添加元素,删除元素,获取迭代器对象的方法
StudentAggregateImpl:具体的容器类,重写所有的方法
*/
/* 代码展示 */
// 抽象迭代器
public interface StudentIterator {
boolean hasNext();
Student next();
}
// 具体迭代器
public class StudentIteratorImpl implements StudentIterator {
private List<Student> list;
private int position = 0;
public StudentIteratorImpl(List<Student> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return position < list.size();
}
@Override
public Student next() {
Student currentStudent = list.get(position);
position ++;
return currentStudent;
}
}
// 抽象容器类
public interface StudentAggregate {
void addStudent(Student student);
void removeStudent(Student student);
StudentIterator getStudentIterator();
}
// 具体容器类(仅仅只是一个容器,用于生成一个统一的迭代器接口,实际内部还是调用我们上面定义的抽象迭代器的子类迭代器)
public class StudentAggregateImpl implements StudentAggregate {
private List<Student> list = new ArrayList<Student>(); // 学生列表
@Override
public void addStudent(Student student) {
this.list.add(student);
}
@Override
public void removeStudent(Student student) {
this.list.remove(student);
}
@Override
public StudentIterator getStudentIterator() {
return new StudentIteratorImpl(list);
}
}
首先我们给出迭代器模式的适用场景:
然后我们给出迭代器模式的优点:
最后我们给出迭代器模式的缺点:
下面我们来介绍访问者模式
首先我们来简单介绍一下访问者模式:
访问者模式包含以下主要角色:
(Element)访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素类个数(Element的实现类个数)是一样的,从这点不难看出,访问者模式要求元素类的个数不能改变。accept),其意义是指,每一个元素都要可以被访问者访问。Element),并且可以迭代这些元素,供访问者访问。我们给出一个简单的案例来解释访问者模式:

具体分析:
/*
【例】给宠物喂食
现在养宠物的人特别多,我们就以这个为例,当然宠物还分为狗,猫等,要给宠物喂食的话,主人可以喂,其他人也可以喂食。
- 访问者角色:给宠物喂食的人
- 具体访问者角色:主人、其他人
- 抽象元素角色:动物抽象类
- 具体元素角色:宠物狗、宠物猫
- 结构对象角色:主人家
*/
/* 代码展示 */
// 抽象访问者
public interface Person {
void feed(Cat cat);
void feed(Dog dog);
}
// 具体访问者(Owner主人,Someone其他人)
public class Owner implements Person {
@Override
public void feed(Cat cat) {
System.out.println("主人喂食猫");
}
@Override
public void feed(Dog dog) {
System.out.println("主人喂食狗");
}
}
public class Someone implements Person {
@Override
public void feed(Cat cat) {
System.out.println("其他人喂食猫");
}
@Override
public void feed(Dog dog) {
System.out.println("其他人喂食狗");
}
}
// 抽象节点
public interface Animal {
void accept(Person person);
}
// 具体节点
public class Dog implements Animal {
@Override
public void accept(Person person) {
person.feed(this);
System.out.println("好好吃,汪汪汪!!!");
}
}
public class Cat implements Animal {
@Override
public void accept(Person person) {
person.feed(this);
System.out.println("好好吃,喵喵喵!!!");
}
}
// 对象结构
public class Home {
// 需要操作的节点
private List<Animal> nodeList = new ArrayList<Animal>();
// 进行操作节点的访问者
public void action(Person person) {
for (Animal node : nodeList) {
// 访问者触动了节点,以节点的形式调用方法
node.accept(person);
}
}
//添加操作
public void add(Animal animal) {
nodeList.add(animal);
}
}
// 测试类
public class Client {
public static void main(String[] args) {
Home home = new Home();
home.add(new Dog());
home.add(new Cat());
Owner owner = new Owner();
home.action(owner);
Someone someone = new Someone();
home.action(someone);
}
}
首先我们给出访问者模式的适用场景:
然后我们给出访问者模式的优点:
扩展性好
在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
复用性好
通过访问者来定义整个对象结构通用的功能,从而提高复用程度。
分离无关行为
通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。
最后我们给出访问者模式的缺点:
对象结构变化很困难
在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
违反了依赖倒置原则
访问者模式依赖了具体类,而没有依赖抽象类。
下面我们来介绍备忘录模式
我们首先给出备忘录模式的概念:
备忘录模式的主要角色如下:
备忘录有两个等效的接口:
我们通过一个案例来介绍白箱备忘录:

/*
【例】游戏挑战BOSS
游戏中的某个场景,一游戏角色有生命力、攻击力、防御力等数据,在打Boss前和后一定会不一样的,我们允许玩家如果感觉与Boss决斗的效果不理想可以让游戏恢复到决斗之前的状态。
【白箱备忘录】
备忘录角色对任何对象都提供一个接口,即宽接口,备忘录角色的内部所存储的状态就对所有对象公开。
由于备忘录角色对任何对象都提供宽接口(所有权限),所以实际上是不安全的
*/
/* 代码展示 */
// 游戏角色类(发起人)
public class GameRole {
private int vit; //生命力
private int atk; //攻击力
private int def; //防御力
//初始化状态
public void initState() {
this.vit = 100;
this.atk = 100;
this.def = 100;
}
//战斗
public void fight() {
this.vit = 0;
this.atk = 0;
this.def = 0;
}
// 保存角色状态(创建出一个备忘录,该备忘录记录当前状况)
public RoleStateMemento saveState() {
return new RoleStateMemento(vit, atk, def);
}
// 回复角色状态(需要一个备忘录,记录需要恢复的状态)
public void recoverState(RoleStateMemento roleStateMemento) {
this.vit = roleStateMemento.getVit();
this.atk = roleStateMemento.getAtk();
this.def = roleStateMemento.getDef();
}
public void stateDisplay() {
System.out.println("角色生命力:" + vit);
System.out.println("角色攻击力:" + atk);
System.out.println("角色防御力:" + def);
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
// 游戏状态存储类(备忘录类)
public class RoleStateMemento {
private int vit;
private int atk;
private int def;
public RoleStateMemento(int vit, int atk, int def) {
this.vit = vit;
this.atk = atk;
this.def = def;
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
// 角色状态管理者类(管理者,内置一个备忘录)
public class RoleStateCaretaker {
// 但是请注意,由于我们的备忘录是一个类,我们的管理者可以对其roleStateMemento进行修改,因此是不符合要求的
private RoleStateMemento roleStateMemento;
public RoleStateMemento getRoleStateMemento() {
return roleStateMemento;
}
public void setRoleStateMemento(RoleStateMemento roleStateMemento) {
this.roleStateMemento = roleStateMemento;
}
}
// 测试类
public class Client {
public static void main(String[] args) {
System.out.println("------------大战Boss前------------");
//大战Boss前
GameRole gameRole = new GameRole();
gameRole.initState();
gameRole.stateDisplay();
//保存进度
RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
roleStateCaretaker.setRoleStateMemento(gameRole.saveState());
System.out.println("------------大战Boss后------------");
//大战Boss时,损耗严重
gameRole.fight();
gameRole.stateDisplay();
System.out.println("------------恢复之前状态------------");
//恢复之前状态
gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());
gameRole.stateDisplay();
}
}
我们通过一个案例来介绍黑箱备忘录:

具体分析:
/*
【例】游戏挑战BOSS
游戏中的某个场景,一游戏角色有生命力、攻击力、防御力等数据,在打Boss前和后一定会不一样的,我们允许玩家如果感觉与Boss决斗的效果不理想可以让游戏恢复到决斗之前的状态。
【黑箱备忘录】
备忘录角色对发起人对象提供一个宽接口,而为其他对象提供一个窄接口。
在Java语言中,实现双重接口的办法就是将备忘录类设计成发起人类的内部成员类。
*/
/* 代码展示 */
// 备忘录 窄接口(只有一个标识,除发起人外都使用该接口,只有一个概念,无法对其进行操作)
public interface Memento {
}
// 游戏角色类
public class GameRole {
// 前面的设置都是一样的,但是在内部定义了备忘录宽接口
private int vit; //生命力
private int atk; //攻击力
private int def; //防御力
//初始化状态
public void initState() {
this.vit = 100;
this.atk = 100;
this.def = 100;
}
//战斗
public void fight() {
this.vit = 0;
this.atk = 0;
this.def = 0;
}
//保存角色状态
public Memento saveState() {
return new RoleStateMemento(vit, atk, def);
}
//回复角色状态
public void recoverState(Memento memento) {
RoleStateMemento roleStateMemento = (RoleStateMemento) memento;
this.vit = roleStateMemento.getVit();
this.atk = roleStateMemento.getAtk();
this.def = roleStateMemento.getDef();
}
public void stateDisplay() {
System.out.println("角色生命力:" + vit);
System.out.println("角色攻击力:" + atk);
System.out.println("角色防御力:" + def);
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
// 备忘录宽接口,基于备忘录窄接口的扩展,由于属于发起人的内部类,发起人可以对其进行操作,备忘录也可以获得对应值
private class RoleStateMemento implements Memento {
private int vit;
private int atk;
private int def;
public RoleStateMemento(int vit, int atk, int def) {
this.vit = vit;
this.atk = atk;
this.def = def;
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
}
// 角色状态管理者类(由于只有一个窄接口,所以无法进行操作,安全!)
public class RoleStateCaretaker {
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
// 客户端
public class Client {
public static void main(String[] args) {
System.out.println("------------大战Boss前------------");
//大战Boss前
GameRole gameRole = new GameRole();
gameRole.initState();
gameRole.stateDisplay();
//保存进度
RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
roleStateCaretaker.setMemento(gameRole.saveState());
System.out.println("------------大战Boss后------------");
//大战Boss时,损耗严重
gameRole.fight();
gameRole.stateDisplay();
System.out.println("------------恢复之前状态------------");
//恢复之前状态
gameRole.recoverState(roleStateCaretaker.getMemento());
gameRole.stateDisplay();
}
}
首先我们给出备忘录模式的适用场景:
然后我们给出备忘录模式的优点:
最后我们给出备忘录模式的缺点:
最后我们介绍一下解释器模式
我们首先给出解释器模式的概念:
我们再来解释一下文法:
我们给出一个文法案例:
expression ::= value | plus | minus
plus ::= expression ‘+’ expression
minus ::= expression ‘-’ expression
value ::= integer
// 注意:这里的符号“::=”表示“定义为”的意思,竖线 | 表示或,左右的其中一个,引号内为字符本身,引号外为语法。
// 上面规则描述为 :
// 表达式可以是一个值,也可以是plus或者minus运算,而plus和minus又是由表达式结合运算符构成,值的类型为整型数。
// 该内容比较生涩难懂,可以看一下案例会好懂很多~
解释器模式包含以下主要角色。
抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
我们通过一个基本案例来理解解释器模式:

具体分析:
/*
【例】设计实现加减法的软件
首先我们来解释几个概念:
- 抽象表达式:书写在运算过程中的所有Expression
- 终结表达式:不需要再计算,直接给出最终结果(Value,Variable)
- 非终结表达式:还不是最终结果,还需要继续运算(Plus,Minus)
AbstractExpreesion:
核心点,明白来说就是Expreesion表达式,这里的表达式包括有Variable变量,Value常量,也包括Plus和Minus运算
只有一个方法interpret,用于给子类去实现,如果是终结表达式直接返回结果,如果是非终结表达式则进一步运算,计算时均调用方法
*/
/* 代码展示 */
// 抽象角色AbstractExpression
public abstract class AbstractExpression {
// 只有一个方法,在运算时调用
public abstract int interpret(Context context);
}
// 终结符表达式角色(常量,例:1,2,3...)
public class Value extends AbstractExpression {
private int value;
public Value(int value) {
this.value = value;
}
// 直接返回结果即可
@Override
public int interpret(Context context) {
return value;
}
@Override
public String toString() {
return new Integer(value).toString();
}
}
// 非终结符表达式角色 加法表达式
public class Plus extends AbstractExpression {
// 既然是加法,那么就有左右两侧,左右两侧同样是Expression,既可以是终结符表达式,也可以是非终结符表达式
private AbstractExpression left;
private AbstractExpression right;
public Plus(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
// 运算时需要将左右两侧进行解释并相加
@Override
public int interpret(Context context) {
return left.interpret(context) + right.interpret(context);
}
@Override
public String toString() {
return "(" + left.toString() + " + " + right.toString() + ")";
}
}
// 非终结符表达式角色 减法表达式
public class Minus extends AbstractExpression {
private AbstractExpression left;
private AbstractExpression right;
// 减法原理相同
public Minus(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
// 运算时需要将左右两侧进行解释并相减
@Override
public int interpret(Context context) {
return left.interpret(context) - right.interpret(context);
}
@Override
public String toString() {
return "(" + left.toString() + " - " + right.toString() + ")";
}
}
// 终结符表达式角色 变量表达式(例:a=1,b=2...)
public class Variable extends AbstractExpression {
private String name;
public Variable(String name) {
this.name = name;
}
@Override
public int interpret(Context ctx) {
return ctx.getValue(this);
}
@Override
public String toString() {
return name;
}
}
// 环境类(存放变量的值)
public class Context {
private Map<Variable, Integer> map = new HashMap<Variable, Integer>();
public void assign(Variable var, Integer value) {
map.put(var, value);
}
public int getValue(Variable var) {
Integer value = map.get(var);
return value;
}
}
// 测试类
public class Client {
public static void main(String[] args) {
Context context = new Context();
Variable a = new Variable("a");
Variable b = new Variable("b");
Variable c = new Variable("c");
Variable d = new Variable("d");
Variable e = new Variable("e");
//Value v = new Value(1);
context.assign(a, 1);
context.assign(b, 2);
context.assign(c, 3);
context.assign(d, 4);
context.assign(e, 5);
AbstractExpression expression = new Minus(new Plus(new Plus(new Plus(a, b), c), d), e);
System.out.println(expression + "= " + expression.interpret(context));
}
}
我们首先给出解释器模式的适用场景:
当语言的文法较为简单,且执行效率不是关键问题时。
当问题重复出现,且可以用一种简单的语言来进行表达时。
当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候。
然后我们给出解释器模式的优点:
易于改变和扩展文法。
由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
实现文法较为容易。
在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂。
增加新的解释表达式较为方便。
如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合 "开闭原则"。
最后我们给出解释器模式的缺点:
对于复杂文法难以维护。
在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护。
执行效率较低。
由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。
关于行为型模式就介绍到这里了,目前我们已经学习了完整的二十三种设计模式,希望能为你带来帮助~
该文章属于学习内容,具体参考B站黑马程序员的Java设计模式详解
这里附上视频链接:10.设计模式-行为型模式概述_哔哩哔哩_bilibili
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio
我在一段非常简单的代码(如我所想)中得到了一个错误的值:org=4caseorgwhenorg=4val='H'endputsval=>nil请不要生气,我希望我错过了一些非常明显的东西,但我真的想不通。谢谢。 最佳答案 这是典型的Ruby错误。case有两种被调用的方法,一种是你传递一个东西作为分支的基础,另一种是你不传递的东西。如果您确实在case中指定了一个表达式语句然后评估所有其他条件并与===进行比较.在这种情况下org评估为false和org===false显然不是真的。所有其他情况也是如此,它们要么是真的,要么是假的。