目录
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); } }