设计模式(一)——工厂模式、原型模式

目录

 

1、简单工厂

 2、工厂方法

3、抽象工厂

4、原型模式


1、简单工厂

// 抽象产品
interface Food {
    void eat();
}
// 具体产品
class Hamburger implements Food {
    @Override
    public void eat() {
        System.out.println("吃汉堡!");
    }
}
public class Test {
    public static void main(String[] args) {
        Food f = new Hamburger();
        f.eat();
    }
}

这种设计,一旦修改了具体产品的类名,客户端代码也要随之一起改变,这样服务端和客户端就是耦合的。

新增一个简单工厂处理;

class FoodFactory {
    public static Food getFood(int n) {
        Food food = null;
        switch (n) {
            case 1:
                food = new Hamburger();
                break;
            case 2:
                food = new Rice();
                break;
        }
        return food;
    }
}
public class Test {
    public static void main(String[] args) {
        Food f = FoodFactory.getFood(1);
        f.eat();
    }
}

优点:

1)把具体产品的类型,从客户端代码中解耦出来

2)服务器端,如果修改了具体产品的类名,客户端也知道

缺点:

1)客户端需要记住常量与具体产品的映射关系,如:1 -> 汉堡

2)如果具体产品特别多,简单工厂将变得十分臃肿

3)客户端一旦要扩展具体产品的时候,势必要修改简单工厂中的代码,这样便违反了“开闭原则”。 

 2、工厂方法

// 抽象产品
interface Food {
    void eat();
}
// 具体产品
class Hamburger implements Food {
    @Override
    public void eat() {
        System.out.println("吃汉堡!");
    }
}
class Rice implements Food {
    @Override
    public void eat() {
        System.out.println("吃面!");
    }
}
interface FoodFactory {
    public Food getFood();
}
class HamburgerFactory implements FoodFactory {
    @Override
    public Food getFood() {
        return new Hamburger();
    }
}
class RiceFactory implements FoodFactory {
    @Override
    public Food getFood() {
        return new Rice();
    }
}

工厂方法的UML类图: 

工厂方法的优点:

1、仍然具有简单工厂的优点

2、当客户端扩展新产品时,不用修改作者(源代码)的代码,只需扩展一个新工厂

 疑问:工厂设计模式都依赖具体的工厂的类名,如若工厂类名被修改了,那么客户端也必需要修改,感觉又回到了原点!

解释:工厂的名字是作为对外暴露的接口,作为约定要保证工厂的名字是稳定的,可能会改变但改变的几率非常低,这个比具体类的改变要小得多的多。

 工厂方法的缺点:

面对产品簇,使用工厂方法设计模式会造成类的爆炸式增长(每个类都必须有一个工厂).

3、抽象工厂

如若只用工厂方法的设计模式,会导致工厂类大幅增加,如下例:

// 抽象产品
interface Food {
    void eat();
}
// 具体产品
class Hamburger implements Food {
    @Override
    public void eat() {
        System.out.println("吃汉堡!");
    }
}
class Rice implements Food {
    @Override
    public void eat() {
        System.out.println("吃面!");
    }
}
// 抽象产品
interface Drink {
    public void drink();
}
// 具体产品
class Cola implements Drink {
    @Override
    public void drink() {
        System.out.println("喝可乐!");
    }
}
class IcePeak implements Drink {
    @Override
    public void drink() {
        System.out.println("喝冰峰!");
    }
}
// 工厂
interface DrinkFactory {
    public Drink getDrink();
}
class ColaFactory implements DrinkFactory {
    @Override
    public Drink getDrink() {
        return new Cola();
    }
}
class IcePeakFactory implements DrinkFactory {
    @Override
    public Drink getDrink() {
        return new IcePeak();
    }
}
interface FoodFactory {
    public Food getFood();
}
class HamburgerFactory implements FoodFactory {
    @Override
    public Food getFood() {
        return new Hamburger();
    }
}
class RiceFactory implements FoodFactory {
    @Override
    public Food getFood() {
        return new Rice();
    }
}

 使用抽象工厂:

// 抽象产品
interface Drink {
    public void drink();
}
// 具体产品
class Cola implements Drink {
    @Override
    public void drink() {
        System.out.println("喝可乐!");
    }
}
class IcePeak implements Drink {
    @Override
    public void drink() {
        System.out.println("喝冰峰!");
    }
}
// 工厂
interface Factory {
    public Food getFood();
    public Drink getDrink();
}
class KFCFactory implements Factory {
    @Override
    public Food getFood() {
        return new Hamburger();
    }
    @Override
    public Drink getDrink() {
        return new Cola();
    }
}
class DongFactory implements Factory {
    @Override
    public Food getFood() {
        return new Rice();
    }
    @Override
    public Drink getDrink() {
        return new IcePeak();
    }
}

抽象工厂优点:

1)具有简单工厂、工厂方法的优点

2)无论多少个产品等级,抽象工厂就只有一套工厂,切实的把工厂类给减少了

 抽象工厂缺点:

当产品等级发生改变时(增加产品等级、删除产品等级),都要引起所有产品簇的修改,这就违反了“开闭原则”

注一:

产品等级和产品簇说明:

4、原型模式

使用原型模式: 

1)必须让目标类实现Cloneable接口,改接口没有任何抽象方法,仅仅是一个标记接口。表示实现该接口的类可以被克隆
2)必须重写java.long.Object类的clone方法,将返回权限改为public

 测试用例:

class WeekReport implements Cloneable {
    private int id;
    private String emp;
    private String summary;
    private String plain;
    private String suggestion;
    private Date time;
......
 @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Test {
    public static void main(String[] args) throws Exception {
        WeekReport wr = new WeekReport();
        wr.setEmp("张三");
        wr.setSummary("完成4篇推文");
        wr.setPlain("推送一篇原创文章");
        wr.setSuggestion("无");
        wr.setTime(new Date());
        // 入库,用输出测试
        System.out.println(wr);

        WeekReport wr2 = (WeekReport) wr.clone();
        wr2.setSummary("完成一篇原创文章");
        wr2.setSummary("3篇推文");
        System.out.println(wr2);
    }
}

 

 注:

1)clone方法不会调用构造器,是直接复制内存中的2进制

2)clone出来的对象与被克隆类不是一个地址,使用的是两个空间

上述代码存在的问题:执行这条语句 wr2.getTime().setTime(0);  这时会导致wr对象的time属性也会发生改变,这是由浅拷贝所导致的。解决方案:

    public Object clone() throws CloneNotSupportedException {
        WeekReport weekReport = (WeekReport) super.clone();
        Date clone = (Date) weekReport.getTime().clone();
        weekReport.setTime(clone);
        return weekReport;
    }

但该方案还是有问题,如果该属性是一个对象,对象里面还包含有其他对象,那么这种嵌套将无止境,无法实现克隆功能。解决方案:

使用Serializable接口:

class WeekReport implements Cloneable, Serializable {}

修改clone方法:

public Object clone() throws CloneNotSupportedException {
    try {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(this);
        oos.close();
        // 从内存中读取数据
        byte[] bytes = out.toByteArray();
        InputStream in = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(in);
        Object clone = ois.readObject();
        ois.close();
        return clone;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值