Java设计模式笔记——七大设计原则
工厂模式是最常用的一类创建型设计模式,通常我们所说的工厂模式是指工厂方法模式,它也是使用频率最高的工厂模式
简单工厂模式(Simple Factory Pattern):定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式
。
简单工厂模式的要点在于:当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。简单工厂模式结构比较简单,其核心是工厂类的设计,其结构如下所示:
先在有一个需求是开发一套图表库,该图表库可以为应用系统提供各种不同外观的图表,例如柱状图、饼状图、折线图等。图表库设计人员希望为应用系统开发人员提供一套灵活易用的图表库,而且可以较为方便地对图表库进行扩展,以便能够在将来增加一些新类型的图表。如下是传统方案:
public class Chart {private String type;AbstractChart abstractChart;public Chart(String type) {this.type = type;if (type.equalsIgnoreCase("bar")) {//初始化柱状图abstractChart = new BarChart();} else if (type.equalsIgnoreCase("pie")) {//初始化饼状图abstractChart = new PieChart();} else if (type.equalsIgnoreCase("line")) {//初始化折线图abstractChart = new LineChart();}}public void display() {if (type.equalsIgnoreCase("bar")) {abstractChart.display();} else if (type.equalsIgnoreCase("pie")) {abstractChart.display();} else if (type.equalsIgnoreCase("line")) {abstractChart.display();}}
}
Chart类是一个“巨大的”类,在该类的设计中存在如下几个问题:
public interface Chart {void display();
}
public class BarChart implements Chart{@Overridepublic void display() {System.out.println("画柱状状图表...");}
}
public class LineChart implements Chart{@Overridepublic void display() {System.out.println("画折线图表...");}
}
public class PieChart implements Chart{@Overridepublic void display() {System.out.println("画饼状图表...");}
}
public class ChartFactory {public static Chart getChart(String type) {Chart chart = null;if (type.equalsIgnoreCase("bar")) {chart = new BarChart();System.out.println("初始化设置柱状图!");} else if (type.equalsIgnoreCase("pie")) {chart = new PieChart();System.out.println("初始化设置饼状图!");} else if (type.equalsIgnoreCase("line")) {chart = new LineChart();System.out.println("初始化设置折线图!");}return chart;}
}
public class Client {public static void main(String[] args) {Chart chart = ChartFactory.getChart("pie");chart.display();}
}
简单工厂模式的主要优点如下
:
简单工厂模式的主要缺点
:
在以下情况下可以考虑使用简单工厂模式:
简单工厂模式虽然简单,但存在一个很严重的问题。当系统中需要引入新产品时,由于静态工厂方法通过所传入参数的不同来创建不同的产品,这必定要修改工厂类的源代码,将违背“开闭原则”。
工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式(Factory Pattern),又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。工厂方法模式是一种类创建型模式。
工厂方法模式提供一个抽象工厂接口来声明抽象工厂方法,而由其子类来具体实现工厂方法,创建具体的产品对象。工厂方法模式结构如下所示:
在工厂方法模式结构图中包含如下几个角色:
开发一个系统运行日志记录器(Logger),该记录器可以通过多种途径保存系统的运行日志,如通过文件记录或数据库记录,用户可以通过修改配置文件灵活地更换日志记录方式。使用简单工厂模式对日志记录器进行了设计结构如下所示:
public interface LoggerSimple {void wirteLog();
}
public class DataBaseLoggerSimple implements LoggerSimple{@Overridepublic void wirteLog() {System.out.println("数据库日志写入...");}
}
public class FileLoggerSimple implements LoggerSimple{@Overridepublic void wirteLog() {System.out.println("写入文件日志...");}
}
public class LoggerFactorySimple {public static LoggerSimple createLogger(String type) {if (type.equalsIgnoreCase("db")) {LoggerSimple logger = new DataBaseLoggerSimple();return logger;} else if (type.equalsIgnoreCase("file")) {LoggerSimple logger = new FileLoggerSimple();return logger;} else {return null;}}
}
public class Client {public static void main(String[] args) {LoggerSimple loggerSimple = LoggerFactorySimple.createLogger("db");loggerSimple.wirteLog();}
}
虽然简单工厂模式实现了对象的创建和使用分离,但是仍然存在如下两个问题:
Logger接口充当抽象产品,其子类FileLogger和DatabaseLogger充当具体产品,LoggerFactory接口充当抽象工厂,其子类FileLoggerFactory和DatabaseLoggerFactory充当具体工厂。
public interface Logger {void wirteLog();
}
public interface LoggerFactory {Logger createLogger();
}
public class FileLogger implements Logger {@Overridepublic void wirteLog() {System.out.println("写入文件日志...");}
}
public class DataBaseLogger implements Logger {@Overridepublic void wirteLog() {System.out.println("数据库日志写入...");}
}
public class FileLoggerFactory implements LoggerFactory{@Overridepublic Logger createLogger() {System.out.println("文件Logger创建...");return new FileLogger();}
}
public class DatabaseLoggerFactory implements LoggerFactory{@Overridepublic Logger createLogger() {System.out.println("数据库Logger创建...");return new DataBaseLogger();}
}
public class Client {public static void main(String[] args) {LoggerFactory factory = new FileLoggerFactory();Logger logger = factory.createLogger();logger.wirteLog();factory = new DatabaseLoggerFactory();logger = factory.createLogger();logger.wirteLog();}
}
工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时还弥补了简单工厂模式的不足。工厂方法模式是使用频率最高的设计模式之一,是很多开源框架和API类库的核心模式。
主要优点
:
主要缺点
:
适用场景:
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。
抽象工厂模式为创建一组对象提供了一种解决方案。与工厂方法模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品。如一个电器工厂,它可以生产电视机、电冰箱、空调等多种电器,而不是只生产某一种电器。为了更好地理解抽象工厂模式,我们先引入两个概念:
在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品,这些产品构成了一个产品族,抽象工厂模式结构如下所示:
现有一个海尔工厂与一个小米工厂,其中会生产冰箱、空调等产品,我们使用方法工厂模式实现如下:
存在问题:
public interface Refrigerator {
}
public interface Air {
}
public interface ElecFactory {Refrigerator createRefrigerator();Air createAir();
}
public class HaierAir implements Air{public HaierAir() {System.out.println("创建海尔空调...");}
}
public class XiaomiAir implements Air{public XiaomiAir() {System.out.println("创建小米空调...");}
}
public class HaierRefrigerator implements Refrigerator {public HaierRefrigerator() {System.out.println("创建海尔冰箱...");}
}
public class XiaomiRefrigerator implements Refrigerator{public XiaomiRefrigerator() {System.out.println("创建小米冰箱...");}
}
public class HaierFactory implements ElecFactory{@Overridepublic Refrigerator createRefrigerator() {return new HaierRefrigerator();}@Overridepublic Air createAir() {return new HaierAir();}
}
public class XiaomiFactory implements ElecFactory{@Overridepublic Refrigerator createRefrigerator() {return new XiaomiRefrigerator();}@Overridepublic Air createAir() {return new XiaomiAir();}
}
public class Client {public static void main(String[] args) {ElecFactory elecFactory = new HaierFactory();elecFactory.createAir();elecFactory.createRefrigerator();elecFactory = new XiaomiFactory();elecFactory.createAir();elecFactory.createRefrigerator();}
}
抽象工厂模式是工厂方法模式的进一步延伸,由于它提供了功能更为强大的工厂类并且具备较好的可扩展性,在软件开发中得以广泛应用,尤其是在一些框架和API类库的设计中,例如在Java语言的AWT(抽象窗口工具包)中就使用了抽象工厂模式,它使用抽象工厂模式来实现在不同的操作系统中应用程序呈现与所在操作系统一致的外观界面。
主要优点:
主要缺点:
增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。
适用场景:
单例模式定义如下: 单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。
单例模式有三个要点:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
创建要求:构造器私有化、类的内部创建对象、向外暴露一个静态的公共方法
public class SingleHungry {private final static SingleHungry SINGLE_HUNGRY = new SingleHungry();private SingleHungry() {}public static SingleHungry getInstance() {return SINGLE_HUNGRY;}
}
public class SingleHungry1 {private static SingleHungry1 singleHungry1;static {singleHungry1 = new SingleHungry1();}private SingleHungry1() {}public static SingleHungry1 getInstance() {return singleHungry1;}
}
饿汉式优缺点:
public class SingleLazy {private static SingleLazy singleLazy;private SingleLazy() {}public static SingleLazy getInstance() {if(singleLazy == null){singleLazy = new SingleLazy();}return singleLazy;}
}
起到了懒加载的效果,但是只能在单线程下使用,在多线程下,一个线程进入了if 判断语句块,还未来得及往下执行,另外一个线程也通过了这个判断语句,就会产生多个实例,在实例开发中不要使用这种方式。
public class SingleLazy1 {private static SingleLazy1 singleLazy;private SingleLazy1() {}public static synchronized SingleLazy1 getInstance() {if(singleLazy == null){singleLazy = new SingleLazy1();}return singleLazy;}
}
解决了线程安全问题,每个线程在想获得类的实例时候,执行getInstance()方法都需要同步。方法进行同步效率太低,实际开发中不推荐使用。
public class SingleLazy2 {private static SingleLazy2 singleLazy;private SingleLazy2() {}public static SingleLazy2 getInstance() {if(singleLazy == null){synchronized (SingleLazy2.class){singleLazy = new SingleLazy2();}}return singleLazy;}
}
和同步方法一样,不推荐使用
public class SingleLazy3 {private static SingleLazy3 singleLazy;private SingleLazy3() {}public static SingleLazy3 getInstance() {if(singleLazy == null){synchronized (SingleLazy3.class){if(singleLazy == null){singleLazy = new SingleLazy3();}}}return singleLazy;}
}
双重检查是多线程开发中经常使用到的,我们进行了两次判断,这样就可以保证线程安全了,这样实例化代码只用执行一次,后面线程在访问时,直接return实例化对象。总的来说:线程安全、延迟加载、效率较高,推荐使用。
public class Single {private Single() {}private static class SingleInstance{private final static Single single = new Single();}public static Single getInstance(){return SingleInstance.single;}
}
说明:
避免了线程不安全,利用内部类特点实现延迟加载,效率高,推荐使用。
public enum SingleEnum {/*** 属性*/INSTANCE;public void hello(){System.out.println("hello word");}
}
public class Client {public static void main(String[] args) {SingleEnum singleEnum = SingleEnum.INSTANCE;SingleEnum singleEnum1 = SingleEnum.INSTANCE;System.out.println(singleEnum1 == singleEnum);singleEnum.hello();}
}
借助jdk1.5中添加枚举的方式来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
原型模式的定义如下: 原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。
原型模式的工作原理很简单:将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程。由于在软件系统中我们经常会遇到需要创建多个相同或者相似对象的情况,因此原型模式在真实开发中的使用频率还是非常高的。原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。
如果原型对象的成员变量是值类型,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。简单来说,在浅克隆中,当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
public class Student implements Cloneable{private String name;private int age;private String color;private IdCard idCard;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public IdCard getIdCard() {return idCard;}public void setIdCard(IdCard idCard) {this.idCard = idCard;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", color='" + color + '\'' +", idCard=" + idCard +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
public class IdCard {private String no;private String address;public String getNo() {return no;}public void setNo(String no) {this.no = no;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return "IdCard{" +"no='" + no + '\'' +", address='" + address + '\'' +'}';}
}
public class Client {public static void main(String[] args) throws CloneNotSupportedException {Student student = new Student();student.setName("张三");student.setAge(18);IdCard idCard = new IdCard();idCard.setNo("123");idCard.setAddress("四川");student.setIdCard(idCard);System.out.println(student);Student student1 = (Student) student.clone();System.out.println(student1);System.out.println(student == student1);System.out.println(student.getIdCard() == student1.getIdCard());}
}
由运行结果知道,原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,对象是一样的。
无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制。
public class DeepClone implements Serializable {private static final long serialVersionUID = 6319963435178724617L;private String name;private int age;private String color;private IdCard idCard;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public IdCard getIdCard() {return idCard;}public void setIdCard(IdCard idCard) {this.idCard = idCard;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", color='" + color + '\'' +", idCard=" + idCard +'}';}protected DeepClone deepClone() throws IOException, ClassNotFoundException {try (ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos)) {oos.writeObject(this);try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis)) {return (DeepClone) ois.readObject();}}}
}
public class IdCard implements Serializable {private static final long serialVersionUID = 7625090532242404161L;private String no;private String address;public String getNo() {return no;}public void setNo(String no) {this.no = no;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic String toString() {return "IdCard{" +"no='" + no + '\'' +", address='" + address + '\'' +'}';}
}
public class Client {public static void main(String[] args) throws IOException, ClassNotFoundException {DeepClone deepClone = new DeepClone();deepClone.setName("张三");deepClone.setAge(18);IdCard idCard = new IdCard();idCard.setNo("123");idCard.setAddress("四川");deepClone.setIdCard(idCard);System.out.println(deepClone);DeepClone deepClone1 = deepClone.deepClone();System.out.println(deepClone1);System.out.println(deepClone == deepClone1);System.out.println(deepClone.getIdCard() == deepClone1.getIdCard());}
}
由结果可知,原型的引用类型,复制一份给克隆对象。深克隆技术实现了原型对象和克隆对象的完全独立,对任意克隆对象的修改都不会给其他对象产生影响,是一种更为理想的克隆实现方式。
建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。
建造者模式一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
在我们经常玩的LOL中,有众多的英雄,这些英雄是一个复杂的对象,无论哪个英雄角色,它的创建都大同小异,我们用建造者进行问题解决。
public interface HeroBuild {void buildName();void buildSex();void buildPosition();Hero getResult();
}
public class Hero {private String name;private String sex;private String position;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getPosition() {return position;}public void setPosition(String position) {this.position = position;}@Overridepublic String toString() {return "Hero{" +"name='" + name + '\'' +", sex='" + sex + '\'' +", position='" + position + '\'' +'}';}
}
public class GailunBuilder implements HeroBuild {private Hero hero = new Hero();@Overridepublic void buildName() {hero.setName("盖伦");}@Overridepublic void buildSex() {hero.setSex("男");}@Overridepublic void buildPosition() {hero.setPosition("上单");}@Overridepublic Hero getResult() {return hero;}
}
public class VnBuilder implements HeroBuild{private Hero hero = new Hero();@Overridepublic void buildName() {hero.setName("维恩");}@Overridepublic void buildSex() {hero.setSex("女");}@Overridepublic void buildPosition() {hero.setPosition("下路");}@Overridepublic Hero getResult() {return hero;}
}
public class Director {private HeroBuild heroBuild;public Director(HeroBuild heroBuild) {this.heroBuild = heroBuild;}public Hero construct() {heroBuild.buildName();heroBuild.buildSex();heroBuild.buildPosition();return heroBuild.getResult();}
}
public class Client {public static void main(String[] args) {Hero gailun = new Director(new GailunBuilder()).construct();System.out.println(gailun);Hero vn = new Director(new VnBuilder()).construct();System.out.println(vn);}
}
建造者模式的主要优点如下:
建造者模式的主要缺点如下:
在以下情况下可以考虑使用建造者模式:
以上就是几种创建型模式:简单工厂模式、工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式,通过本文学习我们能快速掌握这几种模式。