今天学习了下Java设计模式中的策略模式、委派模式、模板模式、原型模式。
做一个学习小结。
委派模式和代理模式其实差不多。委派模式注重的结果而代理注重的是过程,
例子
interface I {
void f();
void g();
}
class A implements I {
public void f() { System.out.println("A: doing f()"); }
public void g() { System.out.println("A: doing g()"); }
}
class B implements I {
public void f() { System.out.println("B: doing f()"); }
public void g() { System.out.println("B: doing g()"); }
}
class C implements I {
// delegation
I i = new A();
public void f() { i.f(); }
public void g() { i.g(); }
// normal attributes
public void toA() { i = new A(); }
public void toB() { i = new B(); }
}
public class Main {
public static void main(String[] args) {
C c = new C();
c.f(); // output: A: doing f()
c.g(); // output: A: doing g()
c.toB();
c.f(); // output: B: doing f()
c.g(); // output: B: doing g()
}
}
在springmvc中,dispatcherservlet用的就是委派模式。
策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
策略模式把一个系列的算法封装到一个系列的具体策略类里面,作为一个抽象策略类的子类或策略接口的实现类。简单地说:准备一组算法,并将每一个算法封装起来,使它们可以互换。(说一个更具体的比喻吧,就是好像LOL中的英雄有四个技能,这个四个技能就是四个策略!!!)
例子:
public class Context {
// 持有一个具体策略的对象
private Strategy strategy;
/**
* 构造函数,传入一个具体策略对象
* @param strategy 具体策略对象
*/
public Context(Strategy strategy){
this.strategy = strategy;
}
/**
* 策略方法
*/
public void contextInterface(){
strategy.strategyInterface();
}
}
public interface Strategy {
/**
* 策略方法
*/
public void strategyInterface();
}
public class ConcreteStrategyA implements Strategy {
@Override
public void strategyInterface() {
// 相关的业务
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void strategyInterface() {
// 相关的业务
}
}
public class ConcreteStrategyC implements Strategy {
@Override
public void strategyInterface() {
// 相关的业务
}
}
原型模式就是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
Java 中 clone方法,但是这只是浅克隆,浅克隆简单来说就是只会克隆复制原对象的数据域,引用类型的变量不会克隆,而是指向原来的指向。深克隆连引用类型边量都会克隆复制。
浅复制(浅克隆) :被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
深复制(深克隆) :被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
可以利用串行化来做深复制,所谓对象序列化就是将对象的状态转换成字节流,以后可以通过这些值再生成相同状态的对象。
原型模式例子:
package 原型模式;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class People implements Serializable,Cloneable{
private static final long serialVersionUID = 1L;
private String name;
private String sex;
private Integer age;
private WorkExperience work;
public People(String name) {
this.name = name;
work = new WorkExperience();
}
//设置个人信息
public void setPersonalInfo(String sex,Integer age) {
this.sex = sex;
this.age = age;
}
//设置工作经历
public void setWorkExperience(String workDate,String company) {
work.setWorkDate(workDate);
work.setCompany(company);
}
//显示
public void display() {
System.out.println(String.format("%s %s %s", name,sex,age));
System.out.println(String.format("工作经历:%s %s", work.getWorkDate(), work.getCompany()));
}
//浅复制
public Object clone() {
Object obj = null;
try {
obj = super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
//深复制
public Object deepClone() throws IOException,ClassNotFoundException{
//将对象写入流中
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(this);
//从流中读出来
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return (oi.readObject());
}
}
package 原型模式;
import java.io.Serializable;
public class WorkExperience implements Serializable {
private static final long serialVersionUID = 1L;
private String workDate;
private String company;
public String getWorkDate() {
return workDate;
}
public void setWorkDate(String workDate) {
this.workDate = workDate;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
}
package 原型模式;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws ClassNotFoundException, IOException {
People p = new People("张三");
p.setPersonalInfo("男", 20);
p.setWorkExperience("2015-2019","阿里巴巴");
//潜复制
People p2 = (People)p.clone();
p2.setWorkExperience("2019-2022", "腾讯");
p2.setPersonalInfo("男", 22);
p.display();
p2.display();
People p3 = new People("李四");
p3.setPersonalInfo("男",24);
p3.setWorkExperience("2001-2003", "字节跃动");
People p4 = (People)p3.deepClone();
p4.setPersonalInfo("男", 24);
p4.setWorkExperience("2008-2009", "美团");
p3.display();
p4.display();
}
}
运行结果:
张三 男 20
工作经历:2019-2022 腾讯
张三 男 22
工作经历:2019-2022 腾讯
李四 男 24
工作经历:2001-2003 字节跃动
李四 男 24
工作经历:2008-2009 美团
定义:
定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。
通俗点的理解就是 :完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。每个步骤的具体实现,由子类完成。
例子
public abstract class DodishTemplate {
/**
* 具体的整个过程
*/
protected void dodish(){
this.preparation();
this.doing();
this.carriedDishes();
}
/**
* 备料
*/
public abstract void preparation();
/**
* 做菜
*/
public abstract void doing();
/**
* 上菜
*/
public abstract void carriedDishes ();
}
/**
* 西红柿炒蛋
* @author aries
*/
public class EggsWithTomato extends DodishTemplate{
@Override
public void preparation() {
System.out.println("洗并切西红柿,打鸡蛋。");
}
@Override
public void doing() {
System.out.println("鸡蛋倒入锅里,然后倒入西红柿一起炒。");
}
@Override
public void carriedDishes() {
System.out.println("将炒好的西红寺鸡蛋装入碟子里,端给客人吃。");
}
}
/**
* 红烧肉
* @author aries
*
*/
public class Bouilli extends DodishTemplate{
@Override
public void preparation() {
System.out.println("切猪肉和土豆。");
}
@Override
public void doing() {
System.out.println("将切好的猪肉倒入锅中炒一会然后倒入土豆连炒带炖。");
}
@Override
public void carriedDishes() {
System.out.println("将做好的红烧肉盛进碗里端给客人吃。");
}
}
public class App {
public static void main(String[] args) {
DodishTemplate eggsWithTomato = new EggsWithTomato();
eggsWithTomato.dodish();
System.out.println("-----------------------------");
DodishTemplate bouilli = new Bouilli();
bouilli.dodish();
}
}
模板模式的优点
(1)具体细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体结构。
(2)代码复用的基本技术,在数据库设计中尤为重要。
(3)存在一种反向的控制结构,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合“开闭原则”。
不足
每个不同的实现都需要定义一个子类,会导致类的个数增加,系统更加庞大。