单例模式
单例模式:保证一个类仅有一个实例,并且这个类提供一个全局的访问接口,某些对象在全局只需要一个,就可以使用单例模式。
单例模式的创建可以分为两种模式:提前加载和延迟加载。
提前加载模式:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
也叫饿汉式,它通过静态变量实现了全局只保证存在一个实例,保证了线程安全。但是,这种方式有一个明显的缺点,如果在程序某一次执行时一直没有用到这个实例,那就造成了资源的浪费,同时,使用全局变量也使程序变得难以调试与维护。
枚举方式:
public enum Singleton {
//定义一个枚举的元素,它就是 Singleton 的一个实例
INSTANCE;
public void doSomething(){}
}
//使用方法
public class Test {
public static void main(String[] args) {
Singleton singleton = Singleton.INSTANCE;
singleton.doSomething();
}
}
延迟加载:
public class Singleton {
private static Singleton instance;
private Singleton (){ }
public static Singleton getInstance() {
//判断当前单例是否已经存在,若存在则返回,不存在则再建立单例
if (instance == null) {
// 1
instance = new Singleton();
}
return instance;
}
}
上面创建单例是存在问题的,当一个线程走到 1 是,尚未来得及创建出对象,另一个线程也走到 1,就会打破单例,创建出多个对象。要解决线程安全问题,我们可以给 getInstance() 这个方法加一个 synchronized 关键字,或者 使用 synchronized() 修饰这段代码块,具体如下:
// 1.
public class Singleton {
private static Singleton instance;
private Singleton (){ }
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
// 2.
public class Singleton {
private static Singleton instance;
private Singleton (){ }
public static Singleton getInstance() {
synchronized(this) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
}
这样,我们就解决了线程安全的问题。但这样又会出现一个问题,每次调用 getInstance() 都会经过加锁这一层,会带来很多不必要的时间消耗,所以,我们在锁外在进行一次判断就会大大节省开支:
public class Singleton {
private volatile static Singleton instance;
private Singleton (){ }
public static Singleton getInstance() {
if (instance == null) {
synchronized(this) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
这里,我们还加了 volatile 关键字来保证 对象在创建时,多线程对其的正确处理。这样,已经完全可以确保线程安全了。
工厂模式
简单工厂模式:
也叫静态工厂模式,客户传入不同的参数,就可以得到不同的对象。
// 产品接口:手机是产品,里面有个方法可以展示操作系统
public interface Phone {
void showSystem();
}
// 具体产品 Android or iOS
public class AndroidPhone implements Phone{
@Override
public void showSystem() {
System.out.println("我是Android系统");
}
}
public class iOSPhone implements Phone{
@Override
public void showSystem() {
System.out.println("我是iOS系统");
}
}
// 工厂负责按需造手机
public class PhoneFactory {
public static Phone create(String phone){
Phone mPhone;
switch (phone){
case "Android":
mPhone = new AndroidPhone();
break;
case "iOS":
mPhone = new iOSPhone();
break;
default:
throw new IllegalStateException("Unexpected value: " + phone);
}
return mPhone;
}
}
// 客户就可以得到想要的手机了
public class Customer {
@Test
public void test(){
// 来个Android的
Phone Android = PhoneFactory.create("Android");
Android.showSystem();
}
}
工厂方法模式:
是简单工厂模式的进一步深化,我们不再提供统一的工厂去创建对象,在工厂方法模式中,工厂类被抽象化,继而由它的子类去实现对象的实例化。这样我们在扩展工厂时就不会破坏开闭原则。
//工厂接口
public interface PhoneFactory {
Phone create();
}
//工厂具体
public class AndroidFactory implements PhoneFactory{
@Override
public Phone create() {
return new AndroidPhone();
}
}
public class iOSFactory implements PhoneFactory{
@Override
public Phone create() {
return new iOSPhone();
}
}
//客户
public class Customer {
@Test
public void test(){
// 来一个Android的
AndroidFactory androidFactory = new AndroidFactory();
Phone android = androidFactory.create();
android.showSystem();
}
}
随着国产手机操作系统的崛起,我们是时候扩展扩展我们的工厂了:
//新增手机
public class HarmonyPhone implements Phone{
@Override
public void showSystem() {
System.out.println("我是鸿蒙的");
}
}
//新增工厂
public class HarmonyOSFactory implements PhoneFactory{
@Override
public Phone create() {
return new HarmonyPhone();
}
}
//客户
public class Customer {
@Test
public void test(){
// 来一个Android的
AndroidFactory androidFactory = new AndroidFactory();
Phone android = androidFactory.create();
android.showSystem();
// 来个鸿蒙的
HarmonyOSFactory harmonyOSFactory = new HarmonyOSFactory();
Phone harmony = harmonyOSFactory.create();
harmony.showSystem();
}
}
这就是工厂方法模式。但是随着我们产品的增多,代码量巨增。
抽象工厂模式:
抽象工厂模式又是工厂方法模式的扩展版,它不再创建一个单一的对象,而是创建一系列相关的对象。
继续拿上面的例子来说,手机除了操作系统,还分国产和进口。
//国产Android手机
public class ChinaAndroid implements AndroidPhone{
@Override
public void showSystem() {
System.out.println("国产Android");
}
}
//国产iOS手机
public class ChinaIOS implements iOSPhone{
@Override
public void showSystem() {
System.out.println("国产iOS");
}
}
//进口Android手机
public class ImportAndroid implements AndroidPhone{
@Override
public void showSystem() {
System.out.println("进口Android");
}
}
//进口iOS手机
public class ImportIOS implements iOSPhone{
@Override
public void showSystem() {
System.out.println("进口iOS");
}
}
//国产工厂
public class ChinaFactory implements PhoneFactory{
@Override
public AndroidPhone createAndroid() {
return new ChinaAndroid();
}
@Override
public iOSPhone createiOS() {
return new ChinaIOS();
}
}
//进口工厂
public class ImportFactory implements PhoneFactory{
@Override
public AndroidPhone createAndroid() {
return new ImportAndroid();
}
@Override
public iOSPhone createiOS() {
return new ImportIOS();
}
}
//客户
public class Customer {
@Test
public void test(){
// 国产工厂
PhoneFactory chinaFactory = new ChinaFactory();
AndroidPhone androidPhone = chinaFactory.createAndroid();
iOSPhone iOSPhone = chinaFactory.createiOS();
androidPhone.showSystem();
iOSPhone.showSystem();
}
}
OK,这就是抽象工厂模式。
介绍到这里,我们发现三种不同的方式存在着很多重叠的地方,而且,设计模式并不存在着明确的定义,关键在于要了解其中的核心思想。工厂模式的核心概念就是利用工厂来创建对象,而不应该将对象的创建交给一个和此对象无关的类,就上面例子而言,难道客户还要负责去自己造出一个手机吗?
建造者模式
正常的建造者模式
建造者模式允许用户在不知道内部构造的情况下,一步步精细控制对象的构造流程来创造出一个复杂对象。当我们构建不同的对象时,可以通过构造不同的建造者来进行实例化。
建造者包含以下类:
- Product(产品类):这是需要为它构建的类。
- Builder(抽象建造者类):用于声明需要构建产品的组成部分。
- ConcreteBuilder(具体建造者):实现抽象建造者中声明的方法。
- Director(指挥者):指导如何构建对象的类。
举个例子,构建一个程序员?
//产品类
public class Programmer {
private int age;
private String language;
private int salary;
public void setAge(int age) {
this.age = age;
}
public void setLanguage(String language) {
this.language = language;
}
public void setSalary(int salary) {
this.salary = salary;
}
public int getAge() {
return age;
}
public int getSalary() {
return salary;
}
public String getLanguage() {
return language;
}
}
//抽象构建者
abstract class Builder {
abstract void buildAge();
abstract void buildSalary();
abstract void buildlanguage();
}
//具体构建者
public class ConcreteBuilder extends Builder{
Programmer programmer = new Programmer();
@Override
void buildAge() {
programmer.setAge(25);
}
@Override
void buildSalary() {
programmer.setSalary(20000);
}
@Override
void buildlanguage() {
programmer.setLanguage("Java");
}
}
//指导类
public class Director {
public void construct(Builder builder){
builder.buildAge();
builder.buildSalary();
builder.buildlanguage();
}
}
//Boss
public class Boss {
@Test
public void test(){
Director director = new Director();
ConcreteBuilder concreteBuilder = new ConcreteBuilder();
director.construct(concreteBuilder);
Programmer programmer = concreteBuilder.programmer;
System.out.println("age: " + programmer.getAge() + "\n" +
"salary: " + programmer.getSalary() + "\n" +
"language: " + programmer.getLanguage());
}
}
拥有方法链的匿名建造者:
我们再举一个例子
public class Computer {
protected String mBoard;
protected String mDisplay;
protected String mCpu;
protected String mGpu;
protected String mOs;
public void setBoard(String board) {
mBoard = board;
}
public void setDisplay(String display) {
mDisplay = display;
}
public void setCpu(String cpu) {
mCpu = cpu;
}
public void setGpu(String gpu) {
mGpu = gpu;
}
public void setOs(String os) {
mOs = os;
}
public static class Builder {
Computer computer;
public Builder() {
computer = new Computer();
}
public Builder board(String board) {
computer.setBoard(board);
return this;
}
public Builder display(String display) {
computer.setDisplay(display);
return this;
}
public Builder cpu(String cpu) {
computer.setCpu(cpu);
return this;
}
public Builder gpu(String gpu) {
computer.setGpu(gpu);
return this;
}
public Builder os(String os) {
computer.setOs(os);
return this;
}
public Computer build() {
return computer;
}
}
}
//使用方法
public void compyter(){
Computer computer = new Computer.Builder()
.board("")
.display("")
.cpu("")
.gpu("")
.os("")
.build();
}
工厂模式是将对象的全部创建过程封装在工厂类中,由工厂类向客户端提供最终的产品;而建造者模式中,建造者类一般只提供产品类中各个组件的建造,而将具体建造过程交付给导演类。由导演类负责将各个组件按照特定的规则组建为产品,然后将组建好的产品交付给客户端。
创建型模式还有原型模式和对象池模式,有时间我们再继续探究。
参考:《Java设计模式及实践》