Java的设计模式共有23种。设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让别人易于理解、保证代码的可靠性,也可以更加简单方便的复用成功的设计和体系结构。
设计模式有6大原则:
1.开闭原则(OCP):对扩展开放,对修改关闭,即在不修改一个软件实体的基础上去扩展功能。
2.里氏代换原则(LSP):软件系统中,一个可以接收基类对象的地方必然可以接收子类对象。
3.依赖倒转原则(DIP):针对接口编程,依赖于抽象而不依赖于具体。
4.接口隔离原则(ISP):使用多个隔离的接口取代一个统一的接口,降低类与类之间的耦合度。
5.迪米特原则(最少知道原则DP):一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
6.合成复用原则(CRP):在系统中应该尽量多使用组合和聚合关联关系,尽量少使用甚至不使用继承关系。
单例模式
单例模式必须确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式的优缺点:
1.优点:单例模式在系统内存中只有一个类,所以可以节约系统资源。对于一些需要频繁创建的销毁的对象单例模式无疑可以提高系统性能。
2.缺点:由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
- 饿汉式单例
class Singleton{
private static final Singleton singleton = new Singleton();//产生唯一一个对象
private Singleton(){}
public static Singleton getInstance(){
return singleton;
}
}
饿汉式是在类创建的同时就创建好一个对象,以后不再改变;
- 懒汉式单例
class Singleton{
private static Singleton singleton = null;
private Singleton(){}
public static Singleton getInstance(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
饿汉式是只有使用单例时才会初始化这个单例。
两者区别:
1.饿汉式:类一旦加载,就把单例初始化完成,保证getInstance时,单例已经存在
懒汉式:只有当调用getInstance时,才回去初始化这个单例。
2.饿汉式线程安全,懒汉式线程不安全,并发环境下可能出现多个Singleton实例(如要线程安全,在getInstance方法前加synchronized关键字)
3.饿汉式在类创建时就实例化一个对象,不管之后是否使用这个单例,都会占据一定内存,但是在第一次调用时速度也会更快,因为资源已经初始化完成。
懒汉式会延迟加载,在第一次使用单例时才会实例化对象,第一次调用时要初始化。
代理模式
代理模式就是多了一个代理类,替原对象进行一些操作。举个栗子:在租房的时候会去找中介,因为中介相对我们来说掌握更多的房屋信息,此处中介就是一个代理。具体来说就是两个子类共同实现一个接口,其中一个子类负责真实业务实现,另一个子类完成辅助真实业务主题的操作。
interface ISubject{ //定义了一个业务接口
public void buyComputer();//核心功能是买电脑
}
class RealSubject implements ISubject{//子类实现业务接口,完成核心业务
public void buyComputer(){
System.out.println("买一台外星人电脑");
}
}
class ProxySubject implements ISubject{
private ISubject subject;
public ProxySubject(ISubject subject){
this.subject = subject;
}
public void produceComputer(){
System.out.println("生产外星人电脑");
}
public void afterSale(){
System.out.println("外星人电脑售后团队");
}
public void buyCopmuter(){
this.produceComputer();//真正操作前的准备
this.subject.buyComputer();//调用真实业务
this.afterSale();//操作后的收尾
}
}
class Factory{
public static ISubject getInstance(){
return new ProxySubject(new RealSubject());
}
}
public class Test{
public static void main(String[] args){
ISubject subject = Factory.getInstance();
subject.buyComputer();;
}
}
工厂模式
工厂模式分为三种:简单工厂模式、工厂方法模式、抽象工厂模式
思考场景:小李准备买笔记本电脑,到商城发现两款笔记本特别喜欢:Macbook Pro,Surface Pro.根据以上场景,类图可以如下表示:
interface Computer{
void printComputer();
}
class MacbookProComputer implements Computer{
public void printComputer(){
System.out.println("This is a MacbookPro");
}
}
class SurfaceBookComputer implements Computer{
public void printComputer(){
System.out.println("This is a SurfaceBook");
}
}
public class Client{
public void buyComputer(Computer computer){
computer.printComputer();
}
public static void main(String[] args){
Client client = new Client();
client.buyComputer(new MacbookProComputer());;
}
}
那么问题来了,如果小李又看上一块外星人(Alienware)笔记本,我们就不得不返回客户端去修改代码,让客户端支持Alienware笔记本。那么,如何将实例化具体类的代码从客户端中抽离或封装起来,使它们不会干扰应用的其他部分呢?
- 简单工厂模式
简单工厂模式就是专门定义一个类用来创建其他类的实例,被创建的实例通常都具有共同的父类。
这里相当于创建生产电脑的过程,客户需要购买什么样的电脑,只要输入类型编号就可以获取该电脑。将类的实例化交给工厂易于解耦。
import java.util.Scanner;
interface Computer{
void printComputer();
}
class MacbookProComputer implements Computer{
public void printComputer(){
System.out.println("This is a MacbookPro");
}
}
class SurfaceBookComputer implements Computer{
public void printComputer(){
System.out.println("This is a SurfaceBook");
}
}
class ComputerFactory{
public static Computer getInseance(String type){
Computer computer = null;
if(type.equals("macbook")){
computer = new MacbookProComputer();
}else if(type.equals("surface")){
computer = new SurfaceBookComputer();
}
return computer;
}
}
public class Client{
public void buyComputer(Computer computer){
computer.printComputer();
}
public static void main(String[] args){
Client client = new Client();
Scanner scanner = new Scanner(System.in);
System.out.println("请输入您想要的电脑型号……");
String type = scanner.nextLine();
Computer computer = ComputerFactory.getInseance(type);
client.buyComputer(computer);
}
}
以上就是简单工厂模式,包括一个抽象产品类、具体产品类、一个工厂
优点:简单易于实现;将类的实例化交给工厂,易于解耦
缺点:添加具体产品需要修改工厂,违反OCP开闭原则
- 工厂方法模式
工厂方法模式:定义一个用来创建对象的接口,让子类决定实例化哪一个类,让子类决定实例化延迟到子类。
工厂方法模式是针对每个产品提供一个工厂类,在客户端中判断使用哪个工厂类去创建对象。将之前的ComputerFactory抽象成一个接口,那么创建相应具体的工厂类去实现该接口的方法。
interface Computer{
void printComputer();
}
class MacbookProComputer implements Computer{
public void printComputer(){
System.out.println("This is a MacbookPro");
}
}
class SurfaceBookComputer implements Computer{
public void printComputer(){
System.out.println("This is a SurfaceBook");
}
}
interface ComputerFactory{
Computer createComputer();
}
class MsFactory implements ComputerFactory{
public Computer createComputer(){
return new SurfaceBookComputer();
}
}
class AppleFactory implements ComputerFactory{
public Computer createComputer(){
return new MacbookProComputer();
}
}
public class Client{
public void buyComputer(Computer computer){
computer.printComputer();
}
public static void main(String[] args){
Client client = new Client();
ComputerFactory factory = new AppleFactory();
client.buyComputer(factory.createComputer());
}
}
简单工厂模式VS工厂方法模式:
1.对于简单工厂模式而言,创建对象的逻辑判断放在了工厂类中,客户不感知具体的类,但是其违背了开闭原则,如果要增加新的具体类,就必须修改工厂类。
2.对于工厂方法模式,是通过扩展来新增具体类的,符合开闭原则,但是在客户端就必须要感知到具体的工厂类,也就是将判断逻辑由简单工厂的工厂类挪到客户端。
3.工厂方法横向扩展很方便,假如该工厂又有新的产品Macbook Air要生产,那么只需要创建相应的工厂类和产品类去实现抽象工厂接口和抽象产品接口即可,而不用去修改原有已经存在的代码。
工厂方法模式包括一个抽象产品类、多个具体产品类、一个抽象工厂、多个具体工厂(每个具体产品对于一个具体工厂)
优点:降低了代码耦合度,对象的生成交给子类去完成;实现了开闭原则(每次添加子产品不需要修改原有代码)
缺点:增加了代码量,每个具体产品都需要一个具体工厂;当增加抽象产品也就是添加一个其他产品族,需要修改工厂,违背开闭原则
- 抽象工厂模式
如果要生产新的一类产品操作系统Mac Os和Windows 8,这时就引申出抽象工厂模式
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
工厂方法模式和抽象工厂模式基本类似。当工厂只生产一个产品时,即为工厂方法模式;如果生产两个或两个以上的商品即变为抽象工厂模式。在查询工厂接口中新增创建系统的方法,并由实例工厂去实现。
interface Computer{
void printComputer();
}
class MacbookProComputer implements Computer{
public void printComputer(){
System.out.println("This is a MacbookPro");
}
}
class SurfaceBookComputer implements Computer{
public void printComputer(){
System.out.println("This is a SurfaceBook");
}
}
interface OperatingSystem{
voif printSystem();
}
class MacOsSystem implements OperatingSystem{
public void printSystem(){
System.out.println("This is a mac os");
}
}
class Window8System implements OperatingSystem{
public void printSystem(){
System.out.println("This is a window 8");
}
}
interface ProductionFactory{
Computer createComputer();
OperatingSystem createSystem();
}
class AppleFactory implements ProductionFactory{
public Computer createComputer(){
return new MacbookProComputer();
}
public OperatingSystem createSystem(){
return new MacOsSystem();
}
}
class MsFactory implements ProductionFactory{
public Computer createComputer(){
return new SurfaceBookComputer();
}
public OperatingSystem createSystem(){
return new Window8System();
}
}
public class Client{
public void buyComputer(Computer computer){
computer.printComputer();
}
public void use(OperatingSystem s){
s.printSystem();
}
public static void main(String[] args){
Client client = new Client();
ProductionFactory factory = new AppleFactory();
Computer computer = factory.createComputer();
OperatingSystem system = factory.createSystem();
client.buyComputer(Computer);
client.use(system);
}
}
抽象工厂模式包括多个抽象产品类、具体产品类、抽象工厂类(声明一组返回抽象产品的方法)、具体工厂类(生成一组具体产品)
优点:代码解耦;实现多个产品族,而工厂方法模式的单个产品可以满足更多的生产需求;满足OCP开闭原则;抽象工厂模式中可以定义实现不止一个接口,一个工厂也可以生成不止一个产品类,对于复杂对象的生产灵活易扩展。
缺点:扩展产品族麻烦且会违背开闭原则;
总结
简单工厂模式最大的优点就是工厂内有具体的逻辑去判断生成什么产品,将类的实例化交给了工厂,这样当需要什么产品时只需要修改工厂的调用而不去修改客户端,对于客户端来说降低了与具体产品的依赖。
工厂方法模式是简单工厂的扩展,工厂方法模式把原先简单工厂中的实现那个类的逻辑判断交给了客户端,如果像添加功能只需要修改客户端和添加具体的功能,不用去修改之前的类。
抽象工厂模式进一步扩展了工厂方法模式,它把原先的工厂方法模式中只能有一个抽象产品不能添加产品族的缺点克服了,抽象工厂模式不仅仅遵循了OCP原则,而且可以添加更多产品(抽象产品),具体工厂也不仅仅可以生成单一产品,而是生成一组产品,抽象工厂也是声明一组产品,对应扩展更加灵活,但是要是扩展族系就会很笨重。