如何需要一套试题——原型模式、模板模式

原型模式、模板模式

 

如果需要复印一套试卷,分发给没一个学生做,那么,我们应该需要哪一个模式?

 

很明显,首先我们需要不断的去复制试卷,为试卷类创建实例,但是,我们知道,一套试卷的创建过程是比较复杂的,因为他包含选择、填空、问答等题型,在执行这个构造函数会消耗较长的时间,同时,试卷构造函数的信息由没有什么变化,那么,用new来创建是不是太昂贵了?

那么,最好的解决方法,就是用克隆,通过复制现在已经有了的实例来创建新的实例。

一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,有对性能呢大大的提高。

其实上面所提到的就是下--原型模式(Prototype),

原型模式代码:

//原型类
abstract class Prototype
{
 private string id;
 public Prototype(string id)
 {
  this.id = id;
 }

 public string Id
 {
  get { return id; }
 }

 public abstract Prototype Clone();
}
//具体原型类
class ConcretePrototype1 : Prototype
{
 public ConcretePrototype1(string id) :base(id)
 {
 }

 public override Prototype Clone()
 {
  return (Prototype)this.MemberwiseClone();
 }
}

客户端代码:

//测试代码
static void Main(string[] args)
{
 ConcretePrototype1 p1 = new ConcretePrototype1("I");
 ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone();
 Console.WriteLine("Cloned:{0}", c1.Id);
 Console.ReadLine();
}

MemberwiseClone()这里对于复制由个问题: MemberwiseClone 方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。 如果字段是值类型的,则对该字段执行逐位复制。 如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。(参见MSDN)

所以,如果您用MemberwiseClone方法复制,但是不符合您的结果,试试下面的方法

  • 调用要复制的对象的类构造函数以创建含有从第一个对象中提出的属性值的第二个对象。 这假定对象的值完全由类构造函数定义。

  • 调用 MemberwiseClone 方法创建的对象的浅表副本,然后将指定新的对象,其值均相同,原始对象的任何属性或字段的值是引用类型。 该示例中的DeepCopy 方法阐释了这种方法。

  • 序列化要深层复制的对象,然后将序列化的数据还原到另一个对象变量。

  • 使用带递归的反射执行的深层复制操作。

试卷复印用原型方法比较适合,可以,如何实现学生答卷的方法呢?

首先,我们先来看看学生答卷的特点:学生答题必须每一题都是不同的答案,不同的学生必须有不同的答案。

其次,我们看看原型方法的特点:原型方法是把A复制给B,B复制给C,如果B与A有一点不同可以单独设置。

最后分析:如果我们设置一份样卷,每一个题有自己的答案,或者把答案设置为空,然后,由A、B、C三个同学复制此样卷,然后把答案部分修改,这样,就可以实现每个学生答题的问题。

可是,这里有个问题,我们虽然可以修改原型模式的一些地方,但是,如果对于答案这种大规模修改的问题,显然就违背了原型模式中的不需要知道任何创建细节,只需复制就可以的设计原则,而此时在使用原型模式就显得画蛇添足了,而且原型模式属于创建型模式,对于答题对对象修改的问题,显得有些力不从心。

那么,由此可见,对于答卷的问题,我们需要的是像原型模式一样,把问题可以放在一个地方,提供一个问题代码复用的平台,而把答案这些时刻变化的东西和问题分开,来实现答题需求。也就是定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,这也就是我们所说的模板方法模式。

模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤


代码实现:

//AbstractClass是抽象类,其实也就是一抽象模板,定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
public abstract class AbstractClass
{
	public abstract void primitiveOperation1();

	public abstract void primitiveOperation2();

	public void templateMethod()
	{
		primitiveOperation1();
		primitiveOperation2();
	}
}
//ConcreteClass,实现父灰所定义的一个或多个抽象方法。每一个AbstractClass都可以有任意多个ConcreteClass与之相对应,而每一个ConcreteClass都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同,从而使得顶级逻辑的实现各不相同。
public class ConcreteClassA extends AbstractClass
{
	@Override
	public void primitiveOperation1()
	{
		System.out.println("具体类A方法1实现");
	}

	@Override
	public void primitiveOperation2()
	{
		System.out.println("具体类A方法2实现");
	}
}
public class ConcreteClassB extends AbstractClass
{
	@Override
	public void primitiveOperation1()
	{
		System.out.println("具体类B方法1实现");
	}

	@Override
	public void primitiveOperation2()
	{
		System.out.println("具体类B方法2实现");
	}
}
//客户端代码
public class Main
{
	public static void main(String[] args)
	{
		AbstractClass c;

		c = new ConcreteClassA();
		c.templateMethod();

		c = new ConcreteClassB();
		c.templateMethod();
	}
}


答题代码:

//试题父类
public class TestPaper
{
	public void testQuestion1()
	{
		System.out.println("杨过得到,后来给了郭靖,练成倚天剑、屠龙刀的玄铁可能是[] "
				+ "a.球磨铸铁 b.马口铁 c.高速合金钥 d.碳素纤维");

		System.out.println("答案:" + answer1());
	}

	public void testQuestion2()
	{
		System.out.println("杨过、程英、陆无双铲除了情花.造成[] " + "a.使这种植物不再害人 b.使一种珍稀物种灭绝 "
				+ "c.破坏了那个生物圈的生态平衡 d.造成该地区沙漠化");

		System.out.println("答案:" + answer2());
	}

	public void testQuestion3()
	{
		System.out.println("蓝凤凰致使华山师徒、桃谷六仙呕吐不止,如果你是大夫,会给他们开什么药[] "
				+ "a.阿司匹林 b.牛黄解毒片 c.氟呱酸 d.让他们喝大量的生牛奶 e.以上全不对");

		System.out.println("答案:" + answer3());
	}

	protected String answer1()
	{
		return "";
	}

	protected String answer2()
	{
		return "";
	}

	protected String answer3()
	{
		return "";
	}
}
//学生甲抄试卷类
public class TestPaperA extends TestPaper
{
	protected String answer1()
	{
		return "b";
	}

	protected String answer2()
	{
		return "c";
	}

	protected String answer3()
	{
		return "a";
	}
}
//学生乙抄试卷类
public class TestPaperB extends TestPaper
{
	protected String answer1()
	{
		return "c";
	}

	protected String answer2()
	{
		return "a";
	}

	protected String answer3()
	{
		return "a";
	}
}
//客户端代码
public class Main
{
	public static void main(String[] args)
	{
		System.out.println("学生甲抄的试卷:");
		TestPaper studentA = new TestPaperA();
		studentA.testQuestion1();
		studentA.testQuestion2();
		studentA.testQuestion3();

		System.out.println("学生乙抄的试卷:");
		TestPaper studentB = new TestPaperB();
		studentB.testQuestion1();
		studentB.testQuestion2();
		studentB.testQuestion3();
	}
}


这里直接借用大话设计模式我代码。

我没可以看出,模板方法模式和原型模式虽然都实现我们现实中“复印”的功能,但是,


模板方法的优点:
1.封装不变部分,扩展可变部分。
把认为是不变部分的算法封转到父类实现中,而可变部分的则可以通过继承来继续扩展。
2.提取公共部分代码,便于维护。
3.行为由父类控制,子类来实现。


所以,原型模式在复印时意在“拷贝”,主要是减轻new带来的压力,而修改只是一小部分,而模板方法模式是在封装不变,扩展可变,这里可变式必要的一部分,所以,复印也有复印的不同。

所以,刚开始学设计模式的时候感觉很多模式很乱,总是感觉这么多设计模式都差不多,不知道到底该在什么时候用,我想,对比,知其本质,也许这样对比存在很大的问题,但是给一个故事作为背景,对于应用不也是一个不错的办法吗?


  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值