23种设计模式在游戏服务器里面例子

游戏里面有很多数据有用XML 形式配置,有用数据库生成。

创建型(5)

   1.简单工厂模式

由一个工厂对象决定创建出哪一种产品类的实例。

 

比如在游戏活动设计的时候:每个活动配置数据的生成

public abstract class AbsLimitActivity{

    private byte id;
    private byte type;
    private String nam 

    private long startTime;
    private long endTime;

}

一个活动类的抽象类,当有新活动时继承

在生成活动数据的时候外面可以用简单工程更据活动ID 来生成放在manger里面管理

 

public <T extends AbsActivity> T createActiviy(byte activityId){
		AbsActivity absActivity=null;
		switch (activityId) {
		case TIME_CAMPAIGN:absActivity=new LimitActTimeCampaign();break;
		case RICH_CAT: absActivity=new LimitActRichCat();break;
		case DAY_CHARGE: absActivity=new LimitActDayCharge();break;
		case CHARGE_REWARD:absActivity=new LimitActChargeReward();break;
		default:return null;
		}	
		return (T) absActivity;
	}

2、抽象工厂模式(Abstract Factory)

一个产品家族提供了统一的创建接口。当需要这个产品家族的某一系列的时候,可以从抽象工厂中选出相对系的系列来创建一个具体的工厂类别。

同样活动里面:

有2大类:一类是开服活动,另一类是配置活动开服,关服的时间

则可以有2个工厂来继承

public abstract class AbsFactory {

	public abstract  <T extends AbsActivity> T createActiviy(int id);
	
}
public class OpenServerActivityFactory extends AbsFactory {

	@Override
	public <T extends AbsActivity> T createActiviy(int id) {
		// TODO Auto-generated method stub
		return null;
	}

}
public class ScheduleActivityFactory extends AbsFactory{

	@Override
	public <T extends AbsActivity> T createActiviy(int id) {
		// TODO Auto-generated method stub
		return null;
	}

}
public class ActiviytManager {

	public static final int OPEN_SERVRE_FACTORY = 1;
	public static final int SCHEDULE_FACTORY = 2;

	public AbsActivity createActivity(int factoryType, int activityId) {
		switch (factoryType) {
		case OPEN_SERVRE_FACTORY: new OpenServerActivityFactory().createActiviy(activityId);
			break;
		case SCHEDULE_FACTORY:new ScheduleActivityFactory().createActiviy(activityId);
			break;

		default:
			break;
		}
		return null;
	}
}

抽象工厂模式是在工厂方法上加了一个维度,抽象工厂模式简单地说及时把工厂进行了一次包装,工厂模式是把产品生产进行包装,对外提供接口。既然抽象工厂模式是站在工厂方法肩膀上的,所以继承工厂方法的所有优点。 
抽象工厂对于工厂进行了包装,使得使用和工厂(工厂和生产两个级别的概念)的解耦,使得工厂类的扩展得到了实现,进一步增强了扩展性。

3、单例模式(Singleton)

一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。

 

public class ActiviytManager {	
    /**
	 * 单例说明
	 */
	public static ActiviytManager instance;
	
	public static ActiviytManager getInstance() {
		if(instance==null) {
			synchronized(ActiviytManager.class){
				instance =new ActiviytManager();
			}
		}
		return instance;
	}
}

单例类只能有一个实例。 
单例类必须自己创建自己的唯一实例。 
单例类必须给所有其他对象提供这一实例。(静态方法get实例) 

单例类比较灵活,毕竟从实现上只是一个普通的Java类,只要满足单例的基本需求,你可以在里面随心所欲的实现一些其它功能,但是静态类不行。

4、建造者模式(Builder)

创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。

	/**
	 * 简单例子 么有添加属性
	 * @param dataList
	 * @return
	 */
	public List<AbsActivity> buildActivityList(List<DbActivityData> dataList){
		List<AbsActivity> list=new ArrayList<>();
		for(DbActivityData data:dataList) {
			list.add(createActivity(data.getType(), data.getId()));
		}
		return list;
	}
	

在接着ActiviytManager  里面再加上这个方法在这里就可以说是用到建造者模式

建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工程模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象,多个部分。因此,是选择工厂模式还是建造者模式,依实际情况而定。

eg:游戏例子,这里取真实工程里面的代码实例


/**
 * 道具创建(对历史遗留做兼容~~,简单的整合)
 * @author ganzhuolin
 */
public class ItemBuild {
	
	private static Logger log = LoggerFactory.getLogger(ItemFactory.class);
	 
	/**
	 * 创建物品对象 带有强化等级和卓越属性的
	 * @param itemModelId			道具模型Id
	 * @param num					数量
	 * @param bind					是否绑定
	 * @param losttime				过期时间,秒数 (0 会读配置的过期时间,其他值则直接赋值过期时间)
	 * @param grade				强化等级
	 * @param zhuoyue				卓越属性
	 * @return {@link List<Item>}	道具集合
	 */
	public static List<Item> createItems(int itemModelId, int num, boolean bind, long losttime, int grade, String zhuoyue) {
		return createItems(itemModelId, num, bind, losttime, grade, 0, zhuoyue);
	}

	/**
	 * 创建物品对象 带有强化等级
	 * @param itemModelId			道具id
	 * @param num					是否绑定
	 * @param bind					是否绑定
	 * @param losttime				过期时间,秒数 (0 会读配置的过期时间,其他值则直接赋值过期时间)
	 * @param grade					等级
	 * @param unuse					没有用到
	 * @return {@link List<Item>}	道具集合
	 */
	public static List<Item> createItems(int itemModelId, int num, boolean bind, long losttime, int grade, int unuse) {
		return createItems(itemModelId, num, bind, losttime, grade, 0, null);
	}

	/**
	 * 掉落物专用
	 * @param itemModelId			道具模型d
	 * @param num					是否绑定
	 * @param bind					是否绑定
	 * @param losttime				过期时间,秒数 (0 会读配置的过期时间,其他值则直接赋值过期时间)
	 * @param grade					等级
	 * @param addAttribute			追加等级
	 * @return {@link List<Item>}	道具集合
	 */
	public static List<Item> createItemsForDropItem(int itemModelId, int num, boolean bind, long losttime, int grade, int addAttribute) {
		return createItems(itemModelId, num, bind, losttime, grade, addAttribute, null);
	}

	
	/**
	 * 创建普通物品(忽略了堆叠数限制)
	 * @param itemModelId
	 * @param num
	 * @return
	 */
	public static Item createCommonGoods(int itemModelId, int num) {
		Item item = new CommonGoods();
		item.setItemModelId(itemModelId);
		item.setNum(num);
		item.setId(IdFactroy.getId());
		return item;
	}

}

5、原型模式(Prototype)

通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象

与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。

一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法。

在游戏中有很多道具,装备,消耗品,等等,他们都是有一些共同的数据,在上独有的数据构成

public abstract class Item  implements Cloneable ,Serializable {
	/**
	 * 原型Id
	 */
	protected int itemModelId;

	/** 物品数量*/ 
	protected int num;
	/**
	 * 名字
	 */
	protected String name;
	
	abstract  Item   clone();
}

浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。

深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。 

继承上面抽象举个例子:

​
public class Prototype extends Item {


	  /* 浅复制 */
	@Override
    public Object clone() throws CloneNotSupportedException {
    	Item proto = (Item) super.clone();
        return proto;
    }

    /* 深复制 */
    public Object deepClone() throws IOException, ClassNotFoundException {

        /* 写入当前对象的二进制流 */
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        /* 读出二进制流产生的新对象 */
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }
	
}

​

结构型模式(7)

1、适配器模式(Adapter)

将一个类的接口, 转换成客户期望的另一个接口. 适配器让原本接口不兼容的类可以合作无间. 对象适配器使用组合, 类适配器使用多重继承.

主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

其其它的6个结构模式都是在对象适配器模式中发展出来的。

核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口时Targetable,通过Adapter类,将Source的功能扩展到Targetable。

 

接着上面游戏活动的来说:

在活动里面有2大类 开服开启多少天活动 和 配置日期活动(那天开始,那天结束);

现在 开服开启活动里面只有 getCloseDay(); 开启后几天后结束的方法

我在ActivityManger 里面要统一回去所有活动的结束日期:

调用getEndData();方法;

主程序关系说不能直接在源代里面添加:

通常我们就用适配的方式解决。

1.类的适配器模式
 

public interface InterfaceActivity {

	/**
	 * 结束日期
	 */
	Date getEndData();
	/**
	 * 活动结束计时
	 */
	int getCloseDay();
}
public class AbsActivity implements InterfaceActivity{

}
public class Activity extends AbsActivity {

	@Override
	public int getCloseDay() {
	 //获取活动开启,到结束的倒计时
		return 0;
	}
	
}
public class OpenServerActivity extends Activity {

	@Override
	public int getCloseDay() {
		return super.getCloseDay();
	}
	
	@Override
	public Date getEndData() {
		// TODO Auto-generated method stub
		return null;
	}
	
}

通过在接口中加上getEndData() 适配的方法来适配 manger里面所有活动需要结束日期的需求

 

2.对象的适配器模式

public class OpenServerActivity extends AbsActivity{

    public Activity activity =new Activity();
 
	public Date getEndData() {
		// TODO Auto-generated method stub
		
	}
	
	public int getCloseDay() {
		return activity.getCloseDay();
		
	}
}

把对象放在新建类中,调用;

3.接口的适配器模式

public interface InterfaceActivity {

	/**
	 * 打开活动
	 */
	void open();
	/**
	 * 开始日期
	 */
	Date getStartData();
	/**
	 * 结束日期
	 */
	Date getEndData();
	/**
	 * 活动结束计时
	 */
	int getCloseDay();

    /**
	 * 加载活动
	 */
	void load();  
	
}
public class AbsActivity implements InterfaceActivity{

	@Override
	public void open() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Date getStartData() {
		return null;
		// TODO Auto-generated method stub
		
	}

	@Override
	public Date getEndData() {
		return null;
		// TODO Auto-generated method stub
		
	}

	@Override
	public int getCloseDay() {
		return 0;
		// TODO Auto-generated method stub
		
	}

}
public class Activity extends AbsActivity {

	@Override
	public int getCloseDay() {
		
		return 0;
	}
	
}

接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。

类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。

对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。

接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

在游戏中在写原创代码代码的时候 选择用第三种适配来写代码。可以方便后来接手程序员来修改和添加 

在接手其人的代码的时候 为了保持源代码不动的情况 一般用第一种适配方法;

第二种代码的形式也是我们经常用到一种代码结构;(在你不经意的时候就用了这种方式)

2、装饰模式(Decorator)

动态地将责任附加到对象上, 若要扩展功能, 装饰者提供了比继承更有弹性的替代方案.

装饰器模式的应用场景: 
1. 需要扩展一个类的功能 
2. 动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删) 
缺点:产生过多相似的对象,不易排错!

public class ScheduleActivity extends AbsActivity {

	private Activity activity;

	public ScheduleActivity(Activity activity) {
		this.activity = activity;
	}
	
	@Override
	public int getCloseDay() {
		System.out.println("装饰类代码前");
		int day= activity.getCloseDay();
		System.out.println("装饰类代码后");
		 return day;
	}
	
	
	public String getCloseDay(int d) {
		int day= activity.getCloseDay();
		String string = String.format("距离互动结束还有%d天", day);
		return string;
	}
}

这里可以理解ScheduleActivity 用来装饰Activity 类

3、代理模式(Proxy)

为另一个对象提供一个替身或占位符以控制对这个对象的访问.

目前很流行的一种编程思想,面向切面编程面向过程编程、面向对象编程和面向切面理解)就是在代理模式基础上做的。

很重要的一个解耦地方,需要使用真是对象的可以使用代理对象实现使用和实现的解耦

public class ProxyActiviy extends AbsActivity{

	private  Activity  activity=new Activity();
	
	@Override
	public void open() {
		//调用目标对象之前可以做相关操作
        System.out.println("before");  
		System.out.println("活动开始");
		activity.open();
		 //调用目标对象之后可以做相关操作
        System.out.println("after");
	}
	
}

是不是感觉和装饰很相似。但是不一样在运用场景上是不一样的。

代理模式的应用场景: 
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法: 
1. 修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则 
2. 就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。 
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!

代理模式的有点太多了,简单的说几个:

  • 实现了对实现类的解耦
  • 使用可以不关心真是对象如何,只需要使用代理对象即可
  • 实现了对实现类的保护
  • 可以通过代理模式对实现类进行定制,裁减,扩展等
  • 总之代理了对实现类想咋玩咋玩嘿嘿

4、外观模式(Facade)

提供了一个统一的接口, 用来访问子系统中的一群接口. 外观定义了一个高层接口, 让子系统更容易使用.


public class ActiviytManager {
	
	private List<Activity> ActivityList = new ArrayList<>();
	/**
	 * 加载配置数据的所有活动
	 * @param ActivityList
	 */
	public void  load (){
	
		for(Activity activity:ActivityList) {
			activity.load();
		}
	}
}

可能这个还太抽象举个在简单点的:

public class Cat {

	private Wheel wheel=new Wheel();
	private Engine engine=new Engine();
	
	/**
	 * 车子动
	 */
	public void run() {
		wheel.run();
		engine.run();
	}
	
	/**
	 * 车轮子
	 * @author luzhongliang
	 *
	 */
	public class Wheel {
		public void run() {
			System.out.println("车轮子动");
		}
	}
	
	/**
	 * 发动机
	 * @author luzhongliang
	 *
	 */
	public class Engine {
		public void run() {
			System.out.println("发动机动");
		}
		
	}
}

一个汽车轮子动,发动机动;发动机轮子动

直接写成:

public void run(){
    System.out.println("车轮子动");
    System.out.println("发动机动");
}

这样是不是2个都在一起了。一个轮子坏了 要换车,可是分开一个轮子坏了 直接换个轮子。
一个系统可以有几个门面类 
  在门面模式中,通常只需要一个门面类,并且此门面类只有一个实例,换言之它是一个单例类。当然这并不意味着在整个系统里只有一个门面类,而仅仅是说对每一个子系统只有一个门面类。或者说,如果一个系统有好几个子系统的话,每一个子系统都有一个门面类,整个系统可以有数个门面类。 
 

优点:

  • 松散耦合 
      门面模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护。
  • 简单易用 
      门面模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟门面类交互就可以了。
  • 更好的划分访问层次 
      通过合理使用Facade,可以帮助我们更好地划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到门面中,这样既方便客户端使用,也很好地隐藏了内部的细节。

5、桥接模式(Bridge)

使用桥接模式通过将实现和抽象放在两个不同的类层次中而使它们可以独立改变.

桥接模式则是用来实现他们之间分离的一种手段,通常是桥接模式则是用来实现他们之间分离的一种手段,在加个setter方法,注入实现这种桥接的分离。

JDBC有用到这种桥接模式:

 桥梁模式在Java应用中的一个非常典型的例子就是JDBC驱动器。JDBC为所有的关系型数据库提供一个通用的界面。一个应用系统动态地选择一个合适的驱动器,然后通过驱动器向数据库引擎发出指令。这个过程就是将抽象角色的行为委派给实现角色的过程。 
  抽象角色可以针对任何数据库引擎发出查询指令,因为抽象角色并不直接与数据库引擎打交道,JDBC驱动器负责这个底层的工作。由于JDBC驱动器的存在,应用系统可以不依赖于数据库引擎的细节而独立地演化;同时数据库引擎也可以独立于应用系统的细节而独立的演化。两个独立的等级结构如下图所示,左边是JDBC API的等级结构,右边是JDBC驱动器的等级结构。应用程序是建立在JDBC API的基础之上的。

public interface MessageImplementor {
    /**
     * 发送消息
     * @param message 要发送消息的内容
     * @param toUser  消息的接受者
     */
    public void send(String message , String toUser);
}
public  class MessageManger {
    //持有一个实现部分的对象
    MessageImplementor impl;
    /**
     * 构造方法,传入实现部分的对象
     * @param impl  实现部分的对象
     */
    public MessageManger (MessageImplementor impl){
        this.impl = impl;
    }
    /**
     * 发送消息,委派给实现部分的方法
     * @param message    要发送消息的内容
     * @param toUser    消息的接受者
     */
    public void sendMessage(String message , String toUser){
        this.impl.send(message, toUser);
    }

    ... get ...set ..方法省略

}
public class MessageSMS implements MessageImplementor {

    @Override
    public void send(String message, String toUser) {

        System.out.println("使用系统内短消息的方法,发送消息'"+message+"'给"+toUser);
    }

}
public class MessageEmail implements MessageImplementor {

    @Override
    public void send(String message, String toUser) {
        System.out.println("使用邮件短消息的方法,发送消息'"+message+"'给"+toUser);
    }

}
public class Test {

    public static void main(String[] args) {
       //消息发送管理
        MessageManager message = new  MessageManager(impl);
        //创建具体的实现对象
        MessageImplementor impl = new MessageSMS();

        message.sendMessage("请假申请","王总");

        //将实现方式切换成邮件,再次发送
        impl = new MessageEmail();
        message.setMessageImplementor(impl);
        message.sendMessage("请假申请","王总");
    }

}

桥接模式的优点不言而喻,那就是它强大的解耦能力,能够很好的实现抽象和实现的解耦,同时使用注入的方式,更加灵活。

6、组合模式(Composite)

使得用户对单个对象和组合对象的使用具有一致性。

 public class TreeNode {
 
        private String name;
        private TreeNode parent;
        private Vector<TreeNode> children = new Vector<TreeNode>();
 
        public TreeNode(String name){
            this.name = name;
        }
 
        public String getName() {
            return name;
        }
 
        public void setName(String name) {
            this.name = name;
        }
 
        public TreeNode getParent() {
            return parent;
        }
 
        public void setParent(TreeNode parent) {
            this.parent = parent;
        }
 
        //添加孩子节点
        public void add(TreeNode node){
            children.add(node);
        }
 
        //删除孩子节点
        public void remove(TreeNode node){
            children.remove(node);
        }
 
        //取得孩子节点
        public Enumeration<TreeNode> getChildren(){
            return children.elements();
        }
    }
 
    public class Tree {
 
        TreeNode root = null;
 
        public Tree(String name) {
            root = new TreeNode(name);
        }
 
        public static void main(String[] args) {
            Tree tree = new Tree("A");
            TreeNode nodeB = new TreeNode("B");
            TreeNode nodeC = new TreeNode("C");
 
            nodeB.add(nodeC);
            tree.root.add(nodeB);
            System.out.println("build the tree finished!");
        }
    }

组合模式存在的意义就是,能够很好的实现了整体和部分的解耦, 
使用是只需对整体进行使用即可,无需关心局部如何实现或者局部有多少个。极大的提高了对局部的扩展性

7、享元模式(Flyweight)

共享单元,通过一种共享池的方式来减少内存的开销。

主要是分清内部和外部的(状态和属性),把他区分开来,内部操作由于都是相同的因此可以共享使用


public interface InterfaceFlyWeight {
    //一个示意性方法,参数state是外部状态
    public void operation(String state);
}

public class ConcreteFlyweight implements InterfaceFlyWeight {
    private String intrinsicState = null;
    /**
     * 构造函数,内蕴状态作为参数传入
     * @param state
     */
    public ConcreteFlyweight(String state){
        this.intrinsicState = state;
    }
    /**
     * 外蕴状态作为参数传入方法中,改变方法的行为,
     * 但是并不改变对象的内蕴状态。
     */
    @Override
    public void operation(String state) {
        //内部状态变化操作
        System.out.println("Intrinsic State = " + this.intrinsicState);
        //外部状态变化操作
        System.out.println("Extrinsic State = " + state);
    }
}
public class FlyweightManager {
    /**共享相同操作和属性的缓存池*/
    private Map<String,Flyweight> files = new HashMap<String,Flyweight>();

    public Flyweight factory(String state){
        //先从缓存中查找对象
        Flyweight fly = files.get(state);
        if(fly == null){
            //如果对象不存在则创建一个新的Flyweight对象
            fly = new ConcreteFlyweight(state);
            //把这个新的Flyweight对象添加到缓存中
            files.put(state, fly);
        }
        return fly;
    }
}

public class Test{

    public static void main(String[] args) {
       
        FlyweightManager factory = new FlyweightManager();
        Flyweight fly = factory.factory(new String('a'));
        fly.operation("First Call");

        fly = factory.factory(new String('b'));
        fly.operation("Second Call");

        fly = factory.factory(new String('a'));
        fly.operation("Third Call");
    }

}

 

1、模式适用环境 
   
在以下情况下可以使用享元模式:

一个系统有大量相同或者相似的对象,由于这类对象的大量使用,造成内存的大量耗费;
对象的大部分状态都可以外部化,可以将这些外部状态传入对象中(细粒度对象);
使用享元模式需要维护一个存储享元对象的享元池,而这需要耗费资源,因此,应当在多次重复使用享元对象时才值得使用享元模式。
2、模式的优点

  (1)它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份; 
  (2)享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

3、模式的缺点

  (1)享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化; 
  (2)为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。
 

行为型(11)

行为型模式之间的关系:

第一类:通过父类与子类的关系进行实现。 :1.策略模式,2.模板模式
第二类:两个类之间。 :1.观察者,2.迭代,3.责任链,4.命令
第三类:类的状态。 :1.备忘录,2.状态模式
第四类:通过中间类 :1.访问者,2.中介者,3.解释器

1、策略模式(strategy)

属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。

策略模式可以理解为对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。

对于游戏里面VIP 玩家在商城买东西有打折,或者对VIP 不同等级有不同的操作:

/**
 * 
 * @author luzhongliang
 *
 */
public interface InterfaceVipOperation {

	
	void discountShop(Player player); 
	void operation(Player player); 
	
}
public class VipOneOperaton implements InterfaceVipOperation {

	@Override
	public void discountShop(Player player) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void operation(Player player) {
		// TODO Auto-generated method stub
		
	}
}
public class VipTwoOperaton implements InterfaceVipOperation {
	@Override
	public void discountShop(Player player) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void operation(Player player) {
		// TODO Auto-generated method stub
		
	}
}
public class VipManger {

    /**存放着VIP 等级 对应的VIP的操作 */
	private HashMap<Integer , InterfaceVipOperation> vipMap=new HashMap<>();
	
	/**客服端直接请求这里,即获取对应的VIP 等级的操作*/
	public void operation(Player player) {
		/**
		 * 获取不同等级的Vip操作
		 */
		vipMap.get(player.getViplevel()).discountShop(player);
	}
	
}

优点

  • 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。
  • 使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。

 

2、模板方法模式(Template Method)

一个抽象类中,有一个主方法,再定义1…n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用。

一种基于继承的代码复用的基本技术,很多地方需要重用另一个类许多方法时,可以使用继承的方式进行复用,同时还可以最其进行个性化的改造。

看看前面的活动例子可以发现那个是个抽象模板

public abstract class AbsLimitActivity implements Instances{
	
	private byte id;
	private byte type;
	private String name;
	private String listname;
	private short icon;
	private String desc;
	private long startTime;
	private long endTime;
	private byte openlast;//开服后几天开启
	private String timeDesc = null;//活动开始结束时间描述;
	public void load(Element e,byte id,byte type) throws Exception {
		this.id = id;
		this.type = type;
		name = e.getAttribute("name");
		listname=e.getAttribute("listname");
		icon = Short.parseShort(e.getAttribute("icon"));
		desc = e.getAttribute("desc");		
		String strStart=e.getAttribute("start");
		if(strStart.isEmpty()){
			startTime = -1l;
		}else{
			startTime = Timestamp.valueOf(strStart).getTime();			
		}
		String strEnd=e.getAttribute("end");
		if(strEnd.isEmpty()){
			endTime = -1l;
		}else{
			endTime= Timestamp.valueOf(strEnd).getTime();
		}
		String strOpenlast=e.getAttribute("openlast");
		if(strOpenlast.isEmpty()){
			openlast=-1;
		}else{
			openlast =Byte.parseByte(strOpenlast);
		}
		Calendar cal = Calendar.getInstance();
	    cal.setTimeInMillis(getStartTime());
	    int startMonth = cal.get(Calendar.MONTH)+1;
	    int startDay = cal.get(Calendar.DAY_OF_MONTH);
	    cal.setTimeInMillis(getEndTime());
	    int endMoth=cal.get(Calendar.MONTH)+1;
	    int endDay =cal.get(Calendar.DAY_OF_MONTH);
	    int endHours=cal.get(Calendar.HOUR_OF_DAY);
	    int endMinutes=cal.get(Calendar.MINUTE);
	    String endHourStr=String.valueOf(endHours);
	    if(endHours>=0&&endHours<10){
	    	endHourStr="0"+endHours;
	    }
	    String endminStr=String.valueOf(endMinutes);
	    if(endMinutes>=0&&endMinutes<10){
	    	endminStr="0"+endminStr;
	    }
	    String activeTimeStr=String.format(GameHints.getHint("ActiveTime:%d/%d-%d/%d %s:%s"),startMonth ,startDay,
	    		endMoth,endDay,endHourStr,endminStr);
		timeDesc = activeTimeStr;
		subLoad(e);
	}
	
	public abstract void subLoad(Element e);
	
	protected boolean isOpen() {
		long nowTime = System.currentTimeMillis();
		long statTime = getStartTime();
		long endTime = getEndTime();
		if (statTime < nowTime && nowTime < endTime) {
			return true;
		} else {
			return false;
		}
	}
}
/**兑换活动*/
public class LimitActExchange extends AbsLimitActivity {
	private List<LimitActExchangeFormula> formulas=new ArrayList<>();
	private List<LimitActExchangeMaterial> materials=new ArrayList<>();
	@Override
	public void subLoad(Element e) {
		Element tmpEls1 = XmlUtils.getChildByName(e, "exchange");
		Element[] els1 = XmlUtils.getChildrenByName(tmpEls1, "reward");
		for(int i=0;i<els1.length;i++){
			LimitActExchangeFormula limitActExchangeFormula=new LimitActExchangeFormula();
			limitActExchangeFormula.loadFormula(els1[i]);
			formulas.add(limitActExchangeFormula);
		}
		Element tmpEls2 = XmlUtils.getChildByName(e, "costshow");
		Element[] els2 = XmlUtils.getChildrenByName(tmpEls2, "reward");
		for(int i=0;i<els2.length;i++){
			LimitActExchangeMaterial limitActExchangeMaterial=new LimitActExchangeMaterial();
			limitActExchangeMaterial.loadMaterial(els2[i]);
			materials.add(limitActExchangeMaterial);
		}
	}
	@Override
	public boolean isOpen(Player player) {
		return isOpen();
	}
	@Override
	public void closeBeforeDispose() {
		
	}
}
/**抽奖活动*/
public class LimitActLuckyDraw extends AbsLimitActivity {
	private byte freeChance=0;
	private short price =50;
	private HashMap< Integer, LimitActLuckyDrawReward> mapLuckyDrawRewards=new HashMap<>();
	private int[] randTable=new int[10000];
	@Override
	public void subLoad(Element e) {
		freeChance=Byte.parseByte(e.getAttribute("freechance"));
		price = Short.parseShort(e.getAttribute("price"));
		Element[] els = XmlUtils.getChildrenByName(e, "reward");
		int index=0;
		for (int i=0; i<els.length; i++) {
			LimitActLuckyDrawReward luckyDrawReward=new LimitActLuckyDrawReward();
			luckyDrawReward.loadReward(els[i]);
			mapLuckyDrawRewards.put(luckyDrawReward.getId(), luckyDrawReward);
			for(int j=0;j<luckyDrawReward.getRate();j++ ){
				if(index<randTable.length){
					randTable[index]=luckyDrawReward.getId();
					index++;
				}
			}
		}
	}
	
	//抽奖,幸运转盘
	public LimitActLuckyDrawReward getRandLuckyDrawReward(){
		int rewardId=0;
		try {
			rewardId=MathUtils.randomOne(randTable);
			return getLuckyDrawReward(rewardId);
		} catch (Exception e) {
			new Exception("not have rewaedId : "+String.valueOf(rewardId));
			return null;
		}
	}
	
	@Override
	public boolean isOpen(Player player) {
		return isOpen();
	}
}

3、观察者模式(Observer)

是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。 
  观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

      观察者很好理解,舞台上只有一个主持人(或数个),台下有诸多观察者,主持人的行为对可以影响到每个人,但是每个人也可以观察到不同的主持人。 

微信的公众号都玩过吧,微博,朋友圈,这都可以作为观察者模式, 可见观察者模式思想应用之广。

游戏里面的推送和是事件的通知 都是观察者模式的运用

public abstract class Subject {
    /**
     * 用来保存注册的观察者对象
     */
    private    List<Observer> list = new ArrayList<Observer>();
    /**
     * 注册观察者对象
     * @param observer    观察者对象
     */
    public void attach(Observer observer){

        list.add(observer);
        System.out.println("Attached an observer");
    }
    /**
     * 删除观察者对象
     * @param observer    观察者对象
     */
    public void detach(Observer observer){

        list.remove(observer);
    }
    /**
     * 通知所有注册的观察者对象
     */
    public void nodifyObservers(String newState){

        for(Observer observer : list){
            observer.update(newState);
        }
    }
}
public class ConcreteSubject extends Subject{

    private String state;

    public String getState() {
        return state;
    }

    public void change(String newState){
        state = newState;
        System.out.println("主题状态为:" + state);
        //状态发生改变,通知各个观察者
        this.nodifyObservers(state);
    }
}
public interface Observer {
    /**
     * 更新接口
     * @param state    更新的状态
     */
    public void update(String state);
}
public class ConcreteObserver implements Observer {
    //观察者的状态
    private String observerState;

    @Override
    public void update(String state) {
        /**
         * 更新观察者的状态,使其与目标的状态保持一致
         */
        observerState = state;
        System.out.println("状态为:"+observerState);
    }

}
public class Client {

    public static void main(String[] args) {
        //创建主题对象
        ConcreteSubject subject = new ConcreteSubject();
        //创建观察者对象
        Observer observer = new ConcreteObserver();
        //将观察者对象登记到主题对象上
        subject.attach(observer);
        //改变主题对象的状态
        subject.change("new state");
    }
}

推模型和拉模型 
  在观察者模式中,又分为推模型和拉模型两种方式。 
推模型 
主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。 
拉模型 
主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。

优点

观察者模式这种一对多的思想十分好,在某个动作同时要引起多个反应的场景下十分适用。

4、迭代子模式(Iterator)

迭代器模式就是顺序访问聚集中的对象,一般来说,集合中非常常见,如果对集合类比较熟悉的话,理解本模式会十分轻松。 
这句话包含两层意思: 
一是需要遍历的对象,即聚集对象 
二是迭代器对象,用于对聚集对象进行遍历访问

用来构建一个链条,当然和责任链不同,责任链模式中个元素是一种包含的关系,而迭代器模式中各个元素是一种等价的关系,一般都有共同的接口。

public interface Collection {
 
        public Iterator iterator();
 
        /*取得集合元素*/
        public Object get(int i);
 
        /*取得集合大小*/
        public int size();
    }  
 
    public interface Iterator {
        //前移
        public Object previous();
 
        //后移
        public Object next();
        public boolean hasNext();
 
        //取得第一个元素
        public Object first();
    }
public class MyCollection implements Collection {
 
        public String string[] = {"A","B","C","D","E"};
        @Override
        public Iterator iterator() {
            return new MyIterator(this);
        }
 
        @Override
        public Object get(int i) {
            return string[i];
        }
 
        @Override
        public int size() {
            return string.length;
        }
    }
  public class MyIterator implements Iterator {
 
        private Collection collection;
        private int pos = -1;
 
        public MyIterator(Collection collection){
            this.collection = collection;
        }
 
        @Override
        public Object previous() {
            if(pos > 0){
                pos--;
            }
            return collection.get(pos);
        }
 
        @Override
        public Object next() {
            if(pos<collection.size()-1){
                pos++;
            }
            return collection.get(pos);
        }
 
        @Override
        public boolean hasNext() {
            if(pos<collection.size()-1){
                return true;
            }else{
                return false;
            }
        }
 
        @Override
        public Object first() {
            pos = 0;
            return collection.get(pos);
        }
 
    }
 public class Test {
 
        public static void main(String[] args) {
            Collection collection = new MyCollection();
            Iterator it = collection.iterator();
 
            while(it.hasNext()){
                System.out.println(it.next());
            }
        }
    }

优点

  • 迭代子模式简化了聚集的接口。迭代子具备了一个遍历接口,这样聚集的接口就不必具备遍历接口。
  • 每一个聚集对象都可以有一个或多个迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。因此,一个聚集对象可以同时有几个迭代在进行之中。
  • 由于遍历算法被封装在迭代子角色里面,因此迭代的算法可以独立于聚集角色变化。

5、责任链模式(Chain of Responsibility)

在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。

责任链模式顾名思义,解决问题是可以通过某个链条进行解决。听起来不是十分精确,这种模式实现了请求和处理的解耦,只需要把某个请求交给责任链,然后等待处理结果即可,无需关心如何处理。

在MINA,或者NETTY 中过滤器的部署就是责任链的运用一种。

在对多个if ...else if ...  的语句可以把他转换为这种模式

 public interface Handler {
        public void operator();
  }
ublic abstract class AbstractHandler {
 
        private Handler handler;
 
        public Handler getHandler() {
            return handler;
        }
 
        public void setHandler(Handler handler) {
            this.handler = handler;
        }
 
 public class MyHandler extends AbstractHandler implements Handler {
 
        private String name;
 
        public MyHandler(String name) {
            this.name = name;
        }
 
        @Override
        public void operator() {
            System.out.println(name+"deal!");
            if(getHandler()!=null){
                getHandler().operator();
            }
        }
    }
public class Test {
 
        public static void main(String[] args) {
            MyHandler h1 = new MyHandler("h1");
            MyHandler h2 = new MyHandler("h2");
            MyHandler h3 = new MyHandler("h3");
 
            h1.setHandler(h2);
            h2.setHandler(h3);
 
            h1.operator();
        }
    }

此处强调一点就是,链接上的请求可以是一条链,可以是一个树,还可以是一个环,模式本身不约束这个,需要我们自己去实现,同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。

优点

  • 责任链实现了请求和处理的解耦
  • 对于请求来说处理是透明的无需关心
  • 在对处理进行修改时无需修改请求情况

6、命令模式(Command)

命令模式把一个请求或者操作封装到一个对象中。命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

命令模式有点像一种浏览器服务器的模式,或者说是一种响应的模式,用户端需要可以通过请求和接收响应来获得自己需要的东西,他无需知道如何实现,也就是说具体请求处理如何进行对用户来说是透明的。实现了对发出命令和执行命令的解耦

命令模式在使用中还是比较广泛的,大名鼎鼎的的windows就是一种基于响应机制做的,用户点击相应按钮就可以触发响应事件。 
B/S架构也是这样,通过浏览器发出的http请求,得到一个html的响应页面

游戏GM后台发出的操作多事是这种模式,一个命令对应了一个操作。

public class dog{

    public void run(){
        System.out.println("跑");
    }

    public void back(){
        System.out.println("后退");
    }

    public void stop(){
        System.out.println("停.");
    }
}
public interface Command {
    /**
     * 执行方法
     */
    public void execute();
}
public enum DogCommands implements InterfaceCommand {
	DOG_RUN{
		@Override
		public void execute(Dog dog) {
			dog.run();
		}
	},
	DOG_STOP{
		@Override
		public void execute(Dog dog) {
			dog.stop();
		}
	},
	DOG_BACK{
		@Override
		public void execute(Dog dog) {
			dog.back();
		}
	}
	;
}
public class test {
	public static void main(String[] args) {
		Dog dog=new Dog();
		/**跑*/
		DogCommands.DOG_RUN.execute(dog);
		/**停*/
		DogCommands.DOG_STOP.execute(dog);
	}
}

优点

  • 更松散的耦合 

    命令模式使得发起命令的对象——客户端,和具体实现命令的对象——接收者对象完全解耦,也就是说发起命令的对象完全不知道具体实现对象是谁,也不知道如何实现。

  • 更动态的控制

    命令模式把请求封装起来,可以动态地对它进行参数化、队列化和日志化等操作,从而使得系统更灵活。

  • 很自然的复合命令  

    命令模式中的命令对象能够很容易地组合成复合命令,也就是宏命令,从而使系统操作更简单,功能更强大。

  • 更好的扩展性

    由于发起命令的对象和具体的实现完全解耦,因此扩展新的命令就很容易,只需要实现新的命令对象,然后在装配的时候,把具体的实现对象设置到命令对象中,然后就可以使用这个命令对象,已有的实现完全不用变化。

 7、备忘录模式(Memento)

备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉(Capture)住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。备忘录模式常常与命令模式和迭代子模式一同使用。 

备忘录模式可以理解为一种用于记录的模式(历史记录),平时使用的world,ppt等软件都有这种记录历史的功能,能够很好的切换到之前某个节点。 这种记录也可以和事务处理相结合,事务处理中牵扯都回滚功能,就可以使用备忘录模式。

备忘录模式主要是用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉(Capture)住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。备忘录模式常常与命令模式和迭代子模式一同使用。

游戏里面操作都用日志来记录所以这个不在怎么用,消耗性能。

 public class Original {
        private String value;
 
        public String getValue() {
            return value;
        }
        public void setValue(String value) {
            this.value = value;
        }
        public Original(String value) {
            this.value = value;
        }
        public Memento createMemento(){
            return new Memento(value);
        }
 
        public void restoreMemento(Memento memento){
            this.value = memento.getValue();
        }
}
 public class Memento {
        private String value;
        public Memento(String value) {
            this.value = value;
        }
        public String getValue() {
            return value;
        }
        public void setValue(String value) {
            this.value = value;
        }
  }
public class Storage {
 
        private Memento memento;
 
        public Storage(Memento memento) {
            this.memento = memento;
        }
        public Memento getMemento() {
            return memento;
        }
        public void setMemento(Memento memento) {
            this.memento = memento;
        }
}
 public class Test {
 
        public static void main(String[] args) {
 
            // 创建原始类
            Original origi = new Original("egg");
 
            // 创建备忘录
            Storage storage = new Storage(origi.createMemento());
 
            // 修改原始类的状态
            System.out.println("初始化状态为:" + origi.getValue());
            origi.setValue("niu");
            System.out.println("修改后的状态为:" + origi.getValue());
 
            // 回复原始类的状态
            origi.restoreMemento(storage.getMemento());
            System.out.println("恢复后的状态为:" + origi.getValue());
        }
}

优点

使用备忘录模式,可以避免暴露一些只应由源发器管理却又必须存储在源发器之外的信息,而且能够在对象需要时恢复到先前的状态。

不足

使用备忘录可能代价很高。如果源发器在生成备忘录时必须复制并存储大量的信息,或者客户非常频繁地创建备忘录和恢复源发器状态,可能会导致非常大的开销。

8、状态模式(State)

对象的状态改变时,同时改变其行为,状态模式就两点: 
1. 可以通过改变状态来获得不同的行为。 
2. 你的好友能同时看到你的变化 

比如安卓的 Activity生命周期,四种状态:活动状态,销毁状态,暂停状态,停止状态。 
面向对象是对现实生活的一种模拟,不同情况需要不同状态。 
在此对Activity声明周期状态做简单说明,首先Activity可以被看做是一个对象,那么最起码需要有创建和销毁两种状态。另外,Activity是由栈进行管理的,位于栈顶和位于栈中又要讲创建分为活动状态和未活动状态。最后对于UI显示是可能出现,可见的为活动状态和不可见的为活动状态。再将未活动状态分为暂停和停止两个状态。

 public class State {
 
        private String value;
 
        public String getValue() {
            return value;
        }
 
        public void setValue(String value) {
            this.value = value;
        }
 
        public void method1(){
            System.out.println("execute the first opt!");
        }
 
        public void method2(){
            System.out.println("execute the second opt!");
        }
    }
 public class Context {
 
        private State state;
 
        public Context(State state) {
            this.state = state;
        }
 
        public State getState() {
            return state;
        }
 
        public void setState(State state) {
            this.state = state;
        }
 
        public void method() {
            if (state.getValue().equals("state1")) {
                state.method1();
            } else if (state.getValue().equals("state2")) {
                state.method2();
            }
        }
}
public class Test {
 
        public static void main(String[] args) {
 
            State state = new State();
            Context context = new Context(state);
 
            //设置第一种状态
            state.setValue("state1");
            context.method();
 
            //设置第二种状态
            state.setValue("state2");
            context.method();
        }
    }

优点

针对不同对象可以采取不同方法,使得程序更加灵活。

不足

程序是灵活了,但是也带来了对每个对象进行判断的复杂和对象需要对外提供不止一个方法。 
当然在适当情况下使用还是比较好的。

9、访问者模式(Visitor)

访问者模式把数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化。访问者模式适用于数据结构相对稳定算法又易变化的系统。因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。

简单来说,访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果

 public interface Visitor {
        public void visit(Subject sub);
    }
public class MyVisitor implements Visitor {
 
        @Override
        public void visit(Subject sub) {
            System.out.println("visit the subject:"+sub.getSubject());
        }
}
 public interface Subject {
        public void accept(Visitor visitor);
        public String getSubject();
    }
 public class MySubject implements Subject {
 
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
 
        @Override
        public String getSubject() {
            return "love";
        }
    }
public class Test {
 
        public static void main(String[] args) {
 
            Visitor visitor = new MyVisitor();
            Subject sub = new MySubject();
            sub.accept(visitor);    
        }
    }

使用时询问

1. 新功能会不会与现有功能出现兼容性问题? 
2. 以后会不会再需要添加? 
3. 如果类不允许修改代码怎么办? 
面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦

优点

  • 好的扩展性 
      能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。

  • 好的复用性 
      可以通过访问者来定义整个对象结构通用的功能,从而提高复用程度。

  • 分离无关行为 
      可以通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。

缺点

  • 对象结构变化很困难 
      不适用于对象结构中的类经常变化的情况,因为对象结构发生了改变,访问者的接口和访问者的实现都要发生相应的改变,代价太高。

  • 破坏封装 
      访问者模式通常需要对象结构开放内部数据给访问者和ObjectStructrue,这破坏了对象的封装性。

10、中介者模式(Mediator)

中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和Mediator类的关系,具体类类之间的关系及调度交给Mediator就行,这有点像spring容器的作用。

中介者模式和局域网中的用户服务器模式很类似: 
最初在计算机中,想要给指定用户发请求,需要在每人使用网线的情况下以点到点的方式传输。这样不太方便,必须得等所有人都不使用网线传输的时候才能够使用,于是引进了一台局域网服务器,不用去关心有人发消息的问题,我只需要把消息给服务器(也就是此处的中介),服务器会自行安排发放。实现了从复杂关系到一种集中式管理的方式,降低了各个模块之间的耦合度,提高了同步的效率。

在面向对象编程中,一个类必然会与其他的类发生依赖关系,完全独立的类是没有意义的。一个类同时依赖多个类的情况也相当普遍,既然存在这样的情况,说明,一对多的依赖关系有它的合理性,适当的使用中介者模式可以使原本凌乱的对象关系清晰,但是如果滥用,则可能会带来反的效果。一般来说,只有对于那种同事类之间是网状结构的关系,才会考虑使用中介者模式。可以将网状结构变为星状结构,使同事类之间的关系变的清晰一些。 
中介者模式是一种比较常用的模式,也是一种比较容易被滥用的模式。对于大多数的情况,同事类之间的关系不会复杂到混乱不堪的网状结构,因此,大多数情况下,将对象间的依赖关系封装的同事类内部就可以的,没有必要非引入中介者模式。滥用中介者模式,只会让事情变的更复杂。

abstract class AbstractColleague {
    protected int number;

    public int getNumber() {
        return number;
    }

    public void setNumber(int number){
        this.number = number;
    }
    //注意这里的参数不再是同事类,而是一个中介者
    public abstract void setNumber(int number, AbstractMediator am);
}

class ColleagueA extends AbstractColleague{

    public void setNumber(int number, AbstractMediator am) {
        this.number = number;
        am.AaffectB();
    }
}

class ColleagueB extends AbstractColleague{

    @Override
    public void setNumber(int number, AbstractMediator am) {
        this.number = number;
        am.BaffectA();
    }
}

abstract class AbstractMediator {
    protected AbstractColleague A;
    protected AbstractColleague B;

    public AbstractMediator(AbstractColleague a, AbstractColleague b) {
        A = a;
        B = b;
    }

    public abstract void AaffectB();

    public abstract void BaffectA();

}
class Mediator extends AbstractMediator {

    public Mediator(AbstractColleague a, AbstractColleague b) {
        super(a, b);
    }

    //处理A对B的影响
    public void AaffectB() {
        int number = A.getNumber();
        B.setNumber(number*100);
    }

    //处理B对A的影响
    public void BaffectA() {
        int number = B.getNumber();
        A.setNumber(number/100);
    }
}

public class Client {
    public static void main(String[] args){
        AbstractColleague collA = new ColleagueA();
        AbstractColleague collB = new ColleagueB();

        AbstractMediator am = new Mediator(collA, collB);

        System.out.println("==========通过设置A影响B==========");
        collA.setNumber(1000, am);
        System.out.println("collA的number值为:"+collA.getNumber());
        System.out.println("collB的number值为A的10倍:"+collB.getNumber());

        System.out.println("==========通过设置B影响A==========");
        collB.setNumber(1000, am);
        System.out.println("collB的number值为:"+collB.getNumber());
        System.out.println("collA的number值为B的0.1倍:"+collA.getNumber());

    }
}

优点

  • 适当地使用中介者模式可以避免同事类之间的过度耦合,使得各同事类之间可以相对独立地使用。
  • 使用中介者模式可以将对象间一对多的关联转变为一对一的关联,使对象间的关系易于理解和维护。
  • 使用中介者模式可以将对象的行为和协作进行抽象,能够比较灵活的处理对象间的相互作用。

不足

中介者模式还是比较合理的,但是有句话说得好:“不要把鸡蛋放在一个篮子里!” 
当中介者类出问题了会使得整个系统出现很大问题。

正因类似原因,于是单个服务器编程了分布式的服务器。使用中介者模式时如果能够考虑到容错性那就再好不过了。

11、解释器模式(Interpreter)

解释器模式是类的行为模式。给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。

解释器模式可以理解为是翻译自己定义的操作,模式自己定义一种操作,提供用户使用,用户使用时需要通过调用解释器完成解释,从而执行操作。 
也可以想作是将同一类操作抽象出来做成一个函数,但是每次都需要调用某个对象来使用这个函数。

方便用户操作而自己定制的操作,在某种情况下需要多次执行某几种操作是,可以将这几种操作定制在一个类中,调用即可。

注:一般主要应用在OOP开发中的编译器的开发中,所以适用面比较窄

 

参考博客网址:

https://blog.csdn.net/Small_Mouse0/article/details/68498105

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章