设计模式
创建型模式
创建型模式是涉及如果创建程序中的对象的设计模式
单例模式(Singleton)
当你有一个全局的对象需要在系统的不同地方使用,例如一个数据库,一些操作都是在该数据库上进行。因为数据库带有全局的属性,所以你只想要该数据库的一个实例。
总结来说,当出现以下情况时,你可以考虑使用单例模式:
一个类只需要一个实例,但不清楚系统的那一部分应该拥有或者管理该实例时。
你希望该实例在代码中随处可用。
实例仅在第一次使用时才进行初始化(延迟话初始)
例如,数据库只需要连接一次,当在其他地方需要使用该数据库连接实例时,直接返回该数据库实例就好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.util.Objects;public final class Database { private static Database database; private Database () {} public static Database getInstance () { if (database == null ) { database = new Database (); database.connect("/usr/local/data/users.db" ); } return database; } private void connect (String url) { Objects.requireNonNull(url); } public static void main (String[] args) { Database a = Database.getInstance(); Database b = Database.getInstance(); System.out.println(a == b); } }
工厂模式
工厂是任何创建对象的东西,工厂对于向调用者隐藏构造细节非常有用。
如果常见对象的东西是方法,那么它被称为工厂方法。
如果创建对象的东西也是一个对象,那么它被称为抽象工厂。当想要把对象的构造分离到完全独立的java接口中时,这非常有用。
抽象工厂模式 (Abstract Factory)
何时使用抽象工厂
你想对调用者隐藏详细信息
你希望将多个相关对象的构造封装到单个Java接口中。
例如下面的例子:
我们有一个页面解析器工厂
接口,他有一个get
方法是返回对参数url
特定的页面解析器。
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.udacity.webcrawler.parser;public interface PageParserFactory { PageParser get (String url) ; }
接下来,我们用一个页面解析器工厂实现类
来实现这个接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.udacity.webcrawler.parser;import com.udacity.webcrawler.Timeout;import com.udacity.webcrawler.profiler.Profiler;import javax.inject.Inject;import java.time.Duration;import java.util.List;import java.util.regex.Pattern;final class PageParserFactoryImpl implements PageParserFactory { private final Profiler profiler; private final List<Pattern> ignoredWords; private final Duration timeout; @Inject PageParserFactoryImpl( Profiler profiler, @IgnoredWords List<Pattern> ignoredWords, @Timeout Duration timeout) { this .profiler = profiler; this .ignoredWords = ignoredWords; this .timeout = timeout; } @Override public PageParser get (String url) { PageParser delegate = new PageParserImpl (url, timeout, ignoredWords); return profiler.wrap(PageParser.class, delegate); } }
当我们给不同的url
构造页面解析器时,只有url是不同的,timeout
、ingoredWords
等参数是相同的。然后,我们重写了get
方法,将url
、timeout
、ignoredWords
等参数传入页面构造器实现类来实例化一个页面构造器。这样,我们不用重复指定传入哪些相同的参数(timeout
和 ignoredWords
),而只需要传入不同的url,通过多次调用get
方法就可以得到不同的页面解析器实例。
在调用者的眼里,调用的方式是如下的:
1 2 3 4 5 PageParserFactory f = new PageParserFactory ( Duration.ofSecond(5 ), List.of("foo" )); ) PageParser p1 = factory.get("udacity.com" );PageParser p2 = factory.get("wikipedia.com" );
建造者模式(Builder)
建造者是一个可变的工厂,逐个属性构造要创建的对象的状态,然后构建该对象。类似前面构建页面解析器的过程,我们这次使用建造者模式来得到不同的页面解析器。如下:
我们在PageParser
类中创建一个Builder
内部类,Builder
中属性字段和PageParser
中的相同,并且实现了每个属性的set
方法。同时实现一个返回页面构造器实例的build
方法。
build
方法内部的实现其实就是调用PageParser
的构造函数来返回一个页面构造器的实例
这样在客户端,我们就可以通过Builder
来构造页面解析器
行为模式 (Behavioral Pattern)
行为模式关注对象间的责任分配,主要处理对象之间的通信。
策略模式 (Strategy Pattern)
定义一个接口 来表示某一种任务或者问题。每个具体实现都定义了解决任务的不同策略。例子如下:
首先创建一个策略接口来表示某一种任务:
1 2 3 public interface Strategy { public int doOperation (int num1, int num2) ; }
接着创建实现接口的实体类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class OperationAdd implements Strategy { @Override public int doOperation (int num1, int num2) { return num1 + num2; } } public class OperationSubtract implements Strategy { @Override public int doOperation (int num1, int num2) { return num1 - num2; } } public class OperationMultiply implements Strategy { @Override public int doOperation (int num1, int num2) { return num1 * num2; } }
接着创建一个Context类,通过context实例来改变策略,从而改变策略的具体行为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Context { private Strategy strategy; public Context (Strategy strategy) { this .strategy = strategy; } public int executeStrategy (int num1, int num2) { return strategy.doOperation(num1, num2); } } public class StrategyPatternDemo { public static void main (String[] args) { Context context = new Context (new OperationAdd ()); System.out.println("10 + 5 = " + context.executeStrategy(10 , 5 )); context = new Context (new OperationSubtract ()); System.out.println("10 - 5 = " + context.executeStrategy(10 , 5 )); context = new Context (new OperationMultiply ()); System.out.println("10 * 5 = " + context.executeStrategy(10 , 5 )); } }
依赖注入(Dependency Injection )
什么是依赖
依赖项是指代码运行所需要的任何内容。当我们谈论依赖注入时,依赖项通常指您的代码导入、创建或使用的对象、类或接口。
什么是依赖注入
依赖注入是一种设计模式,它将依赖项的创建移至代码外部。你不需要创建对象,而是通过依赖注入框架帮你创建对象,然后将这些对象注入到类中。