最近渐渐感觉,玩网络游戏有点累了,其一,太烧钱。其二,太费时间。最近这1年玩的游戏逐渐从网游转到单机了,因为不需要考虑每天固定的游戏任务,节日活动等。想玩了我就玩会儿,不想玩就可以看点书或者做点自己想做的事情,时间很自由,不像网游里时不时会想到今天的任务还没刷呢,明天马上又是啥节日了,做活动可以获得什么什么的。似乎不是自己在玩游戏,而是游戏在玩自己。就昨天吧,把自己心爱的号卖掉了。
所以现在基本都是在玩steam里的单机游戏了,碰到打折的时候,有自己喜欢的游戏就买下来 偶尔和之前的同事朋友一起玩玩儿,也挺不错的。
由于本人比较喜欢玩沙盒类的游戏,一个存档可以玩很久很久,而单机游戏一般都是只保存在本地,虽然现在也有些单机游戏支持云存档,但我现在玩的暂时还不支持,如果不在家玩,比如去了网吧,或者在公司加班到很晚,想玩上一把的时候,除非把自己电脑的存档拷贝到U盘里,然后走到哪玩就带到哪里,每次都得手动来同步这些数据。
突然脑子里就想到了在工作中的时候,有些对象和对象之间存在某种关联关系,当我其中一个对象的数据发生了改变的时候,其他和它关联的对象数据也需要一起发生改变。就像我上面提到的每次玩游戏保存的时候,如果能在每次保存游戏的时候,其他地方也能一起更新保存我最后一次的游戏数据就好了(单机游戏云存档目前还没有100%普及)。
这让我想起了设计模式中的观察者模式。
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
下面就来使用一个简单的案例来说明,还是使用这个游戏保存来举例吧,保存游戏的这个动作就被定义为被观察者,而在家,在网吧和公司玩游戏就被定义为观察者,只要在其中任何一个场景玩游戏并且保存了游戏的话,那么3个地方的数据都需要一起更新保存。这里我就以最后的游戏时间作为保存游戏的数据。
1.首先定义一个被观察的对象,也就是保存游戏时的对象
import java.util.Date;
/***
* 被观察者-游戏保存时触发
*/
public abstract class GameSave {
/**
* 操作游戏保存时的主要类
*/
protected OpraterGameSave OpraterGameSave;
/**
* 游戏保存的方法
*/
protected abstract void saveIng(Date date);
}
2.其次需要一个具体操作保存游戏时的对象
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class OpraterGameSave {
/**
* 存放每个地方保存游戏的对象(家里,工作地点,网吧)
*/
private List gameSaveList=new ArrayList<>();
/**
* 最后保存游戏的时间
*/
private Date gameTime;
/**
* 保存游戏时间
* @param gameTime
*/
public void setGameSaveTime(Date gameTime){
this.gameTime=gameTime;
loop();
}
/**
* 批量修改最后保存的游戏时间
*/
protected void loop(){
for(GameSave game:gameSaveList){
game.saveIng(gameTime);
}
}
/**
* 添加游戏到集合里面
* @param gameSave
*/
public void addGame(GameSave gameSave){
gameSaveList.add(gameSave);
}
3.每个场景玩游戏的对象(家里,网吧,公司)
import java.util.Date;
/**
* 在家玩游戏(观察者1)
*/
public class PlayGameAtHome extends GameSave{
/**
* 构造方法,初始化的时候就把对象添加到集合里
* @param opraterGameSave
*/
public PlayGameAtHome(OpraterGameSave opraterGameSave){
this.OpraterGameSave=opraterGameSave;
}
/**
* 在家玩游戏保存游戏
* @param date
*/
@Override
protected void saveIng(Date date) {
System.out.println("家里玩游戏,最后保存游戏的时间为:"+date);
}
}
import java.util.Date;
/**
* 网吧玩游戏(观察者2)
*/
public class PlayGameAtInternetCafe extends GameSave{
public PlayGameAtInternetCafe(OpraterGameSave opraterGameSave){
this.OpraterGameSave=opraterGameSave;
}
@Override
protected void saveIng(Date date) {
System.out.println("网吧玩游戏,保存游戏的最后时间为"+date);
}
}
/**
* 办公室玩游戏(观察者3)
*/
public class PlayGameAtOffice extends GameSave{
public PlayGameAtOffice(OpraterGameSave opraterGameSave){
this.OpraterGameSave=opraterGameSave;
}
@Override
protected void saveIng(Date date) {
System.out.println("办公室玩游戏,保存游戏的最后时间为"+date);
}
}
4.主程序调用以及运行结果
import java.util.Date;
/**
* 测试观察游戏保存
*/
public class TestSave {
public static void main(String[] args) {
//创建一个处理保存游戏时的对象
OpraterGameSave opraterGameSave=new OpraterGameSave();
//添加一个在家里玩游戏的场景情况
opraterGameSave.addGame(new PlayGameAtHome(opraterGameSave));
//添加一个在网吧玩游戏的场景情况
opraterGameSave.addGame(new PlayGameAtInternetCafe(opraterGameSave));
//添加一个在办公室玩游戏的场景情况
opraterGameSave.addGame(new PlayGameAtOffice(opraterGameSave));
//保存最后游戏的时间,在家和网吧的最后时间都一起发生改变
opraterGameSave.setGameSaveTime(new Date());
}
}
相当于我通过操作保存游戏的类来把每个场景玩游戏的对象都集中在一起,只要调用保存游戏的时候 每个被我加入进来的对象都会被执行保存游戏这个操作。当然不论任何事物都存在2面性。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
--------------------------------------------------------------------更新与2020.9.2---------------------------------------------------------------------------------
还有一个例子,就拿商品对象(goods),商品价格对象(goodsPrice)和商品图片对象(goodsPic)举例 后2个对象都有一个goodsId关联商品的id,现在假设商品对象的ID发生了改变,那么价格和图片的goodsId也需要一起跟着改变。以前的做法是 先查询出商品改变之前的id,然后select * from 商品价格表 where goodsId=商品之前的id 和select * from 商品图片表 where goodsId=商品之前的id 然后去遍历重新setGoodsId(现在修改之后的id),价格表一个循环 图片表一个循环,最后商品表的id修改 咱们现在使用了观察者模式 可以让代码更简洁。通过一个对象,把商品,图片和价格3个对象都添加进来,然后调用一个setGoodsId()就能统一修改3个对象里的值了。代码如下:
1.首先想到的是把3个对象统一需要做的事情提取出来,抽成一个方法
public abstract class DataChange {
protected OpraterData opraterData;//专门处理修改数据的类
protected abstract void update(Long goodsId);//商品,价格和图片都需要进行的一个修改的操作
}
2.专门处理修改数据的类
import java.util.ArrayList;
import java.util.List;
public class OpraterData {
List dataChangeList=new ArrayList<>();//用来存放商品,商品图片和价格的集合
private Long goodsId;//需要修改的数据
//公共的赋值修改方法,调用的时候统一修改集合里对象里的数据
public void setGoodsId(Long goodsId){
this.goodsId=goodsId;
loopUpdate();
}
//遍历修改集合里对象的数据
private void loopUpdate(){
for (DataChange data:dataChangeList){
data.update(goodsId);
}
}
//公共的添加对象到集合里,只要继承了DataChange对象都可以添加进来
public void addData(DataChange data){
dataChangeList.add(data);
}
}
3.商品对象,价格对象和图片对象
public class Goods extends DataChange{
//构造方法添加参数为具体操作修改数据的类
public Goods(OpraterData data){
this.opraterData=data;
}
private Long id;//商品的主键ID
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
//重写继承的修改数据的方法
@Override
protected void update(Long goodsId) {
this.setId(goodsId);
System.out.println("商品修改之后的id为:"+goodsId);
}
}
public class Goodsprice extends DataChange{
public Goodsprice(OpraterData data){
this.opraterData=data;
}
public Long getGoodsId() {
return goodsId;
}
public void setGoodsId(Long goodsId) {
this.goodsId = goodsId;
}
private Long goodsId;
@Override
protected void update(Long goodsId) {
this.goodsId=goodsId;
System.out.println("商品价格修改后的goodsId值为:"+this.getGoodsId());
}
}
public class GoodsPic extends DataChange{
private Long goodsId;
public Long getGoodsid() {
return Goodsid;
}
public void setGoodsid(Long goodsid) {
Goodsid = goodsid;
}
private Long Goodsid;
public GoodsPic(OpraterData data){
this.opraterData=data;
}
@Override
protected void update(Long goodsId) {
this.setGoodsid(goodsId);
System.out.println("商品图片修改之后的goodsId的值为:"+this.getGoodsid());
}
}
4.主程序调用并打印
public class TestSave {
public static void main(String[] args) {
OpraterData oprater=new OpraterData();
/**
* 1.商品,商品图片和商品价格3个对象初始化ID改为100并打印
*/
Goods goods = new Goods(oprater);
goods.setId(100L);
GoodsPic pic = new GoodsPic(oprater);
pic.setGoodsid(100L);
Goodsprice goodsprice=new Goodsprice(oprater);
goodsprice.setGoodsId(100L);
System.out.println("商品修改之前的id:"+goods.getId()+",商品图片修改之前的goodsId:"+pic.getGoodsid()+",商品价格对象修改之前的goodsId:"+goodsprice.getGoodsId());
/**
* 2.当修改主表商品的ID值时,关联的商品图片和商品价格都跟着一起改变,这里使用一个对象,把需要改变的商品,商品图片和商品价格都一起添加进去
*/
oprater.addData(goods);
oprater.addData(pic);
oprater.addData(goodsprice);
/**
* 3.修改ID为200并打印
*/
oprater.setGoodsId(200L);
}
}
3个对象为构造方法添加OpraterData对象(它们的父类有该对象的成员属性),为了确保共用同一个OpraterData对象,并且该对象里的List集合能添加存放这3个对象。