软件体系结构与架构五大设计模式

目录

五大设计模式

最关键的软件开发工具是受过良好设计原则训练的思维

一、单例模式

Singleton类定义一个个getInstance()操作,允许客户端访问他的唯一实例,getInstance()是一个静态的方法,主要创建自己的一个唯一实例。

比如我们希望整个应用程序只有一个连接数据库connection实例。

又比如要求一个应用程序中只存在某个用户数据结构的唯一结构。
某个类只有一个实例,具体的代码实现:

class ClassicSingleton { 
	private static ClassicSingleton instance;
	private ClassicSingleton(){ 
		//构造方法为“私有”,阻止了外界用new创建本类实例的可能
	}
	public static synchronized ClassicSingleton getInstance(){ 
		if(instance==null){ 
		instance = new ClassicSingleton();
		//return new ClassicSingleton(); 
		}
		return instance;
	}
}
/** * * @author Yue * @version 0.1 * */
public class TestSingleton { 

	public static void main(String[] args) { 
		// TODO Auto-generated method stub
		ClassicSingleton s1 = ClassicSingleton.getInstance();
		ClassicSingleton s2 = ClassicSingleton.getInstance();
		System.out.println(s1==s2);//这个是相等的
	}
}

二、工厂模式(简单工厂、工厂模式、抽象工厂)

所有的产品和产品接口组成一个产品群,这个就是“简单工厂”,只有一个工厂。

工厂和产品最好在一个包里,产品类就可以不是public的,这样只有工厂才可以访问产品,其他不在这个包里就无法直接访问这些产品,起到屏蔽产品类的作用。

可以看着下面的代码来理解一下简单工厂模式具体代码说明

//产品接口(如果在接口这个抽象类中需要包含属性数据,则将接口改为抽象类Abstract)
public interface ProductInterface   { 
	public void method();
}
//衣服产品
public class Clothes implements ProductInterface   { 
	public void method(){ 
	
	}
}
//裤子产品
public class Trousers implements ProductInterface { 
	public void method(){ 
	
	}
}
//帽子产品
public class Cap implements ProductInterface   { 
	public void method(){ 
	
	}
}

public class Factory { 
	public ProductInterface createProduct(String productname){ 
		ProductInterface product = null;
		if(productname == "clothes"){ 
			product = new Clothes();
		}
		if(productname == "trousers"){ 
			product = new Trousers();
		}		
		if(productname == "cap"){ 
			product = new Cap();
		}
		return product;
	}
}
//要生产哪种产品,需要告诉工厂,即给工厂方法传递一个参数“productname”,
//工厂方法变为有参数的方法。
//工厂类不管是生产一种产品还是生产几种产品,都只有一个工厂,这种工厂叫“简单工厂”。
public class XYZ {   // 客户端代码
	private ProductInterface product;
	private Factory factory;
	public void work(){ 
		factory = new Factory();
		product = factory.createProduct("clothes");
		product.method();
	}
}

这样做的优点:我们可以对创建的对象进行一些 “加工” ,而且客户端并不知道,因为工厂隐藏了这些细节。如果,没有工厂的话,那我们是不是就得自己在客户端上写这些代码,这就好比本来可以在工厂里生产的东西,拿来自己手工制作,不仅麻烦以后还不好维护。

但是缺点也很明显:如果需要在方法里写很多与对象创建有关的业务代码,而且需要的创建的对象还不少的话,我们要在这个简单工厂类里编写很多个方法,每个方法里都得写很多相应的业务代码,而每次增加子类或者删除子类对象的创建都需要打开这简单工厂类来进行修改。这会导致这个简单工厂类很庞大臃肿、耦合性高,而且增加、删除某个子类对象的创建都需要打开简单工厂类来进行修改代码也违反了开-闭原则。

这时候就需要工厂模式
工厂模式实现如下
1.首先,先定义一个工厂接口

public interface Factory { 
 
    public ProductInterface createProduct() ;
 
}

2.然后是具体的工厂类


// 衣服类工厂
public class ClothesFactory implements Factory{ 
 
    public ProductInterface createProduct() { 
        System.out.println("生产衣服");
        return new Clothes();
    }
}
 
// 鞋类工厂
public class ShoeFactory implements Factory{ 
 
    public ProductInterface createProduct() { 
        System.out.println("生产鞋子");
        return new Shoes();
    }
}
........

3.工厂接口与产品的内容与简单一样
4.客户端

public class Client { 
 
    public static void main(String[] args) throws Exception { 
 
        // 使用反射机制实例化工厂对象,因为字符串是可以通过变量改变的
        Factory ClothesFactory = (Factory) Class.forName("org.zero01.factory.ClothesFactory ").newInstance();
        Factory ShoseFactory=(Factory) Class.forName("org.zero01.factory.ShoseFactory").newInstance();
 
        // 通过工厂对象创建相应的实例对象
        ProductInterface clothes= ClothesFactory .createProduct();
        ProductInterface shoes= ShoseFactory.createProduct();
 
        System.out.println(clothes.methods());
        System.out.println(shoes.methods());
    }
}

工厂模式中,要增加产品类时也要相应地增加工厂类,客户端的代码也增加了不少。工厂方法把简单工厂的内部逻辑判断转移到了客户端代码来进行。

你想要加功能,本来是改工厂类的,而现在是修改客户端。而且各个不同功能的实例对象的创建代码,也没有耦合在同一个工厂类里,这也是工厂方法模式对简单工厂模式解耦的一个体现。工厂方法模式克服了简单工厂会违背开-闭原则的缺点,又保持了封装对象创建过程的优点。

但工厂方法模式的缺点是每增加一个产品类,就需要增加一个对应的工厂类,增加了额外的开发量。

有兴趣的可以参考这个文件深入了解简单工厂模式,工厂模式以及抽象工厂模式(具体)

三、适配器模式

适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。当希望复用的类接口不对时,采用适配器使控制之外的一个原有对象与某个接口匹配。
《软件体系结构与架构五大设计模式》

代码实现

//客户端:
public class XYZ { 
	public void work()
	{ 
		Target tag = new RealizeClass();
		tag.Request(); 
	}
}
//服务接口:
public interface Target 
{ 
	public void Request();
}
// 客户端想要的服务:
public class Service 
{ 
	public void SpecificRequest()     //但是接口不匹配
	{ 
		//具体操作方法
	}
}

//重写服务端实现类,作为适配器,代码为:
public class RealizeClass implements Target
{ 
	private Service service;
	public void Request()
	{ 
		service = new Service();
		service.SpecificRequest();   //进行适配
	}
}
// 例子2
public interface Duck{      //鸭子具有呱呱叫和飞行功能
	public void quack();
	public void fly();
} 

Public class MallardDuck implements Duck{ 
	public void quack(){ 
		System.out.println(“Quack”);
	}
	 public void fly(){ 
		System.out.println(“I am flying”);
	}
}

public interface Turkey{      //火鸡具有咯咯叫和飞行功能
	public void gobble();
	public void fly();
} 

Public class WildTurkey implements Turkey{ 
	public void gobble(){ 
		System.out.println(“Gobble gobble”);
	}
	 public void fly(){ 
		System.out.println(“I am flying a short distance”);
	}
}

/** * 假设你缺少鸭子对象,想用一些火鸡对象来冒充 * 因为火鸡的接口不同,所以我们不能公然拿来用,写个适配器 **/
public class TurkeyAdapter implements Duck{  //实现想转化的类型
	Turkey turkey;
	//利用构造器取得适配器的构造引用
	public TurkeyAdapter (Turkey turkey){ 
		this.turkey = turkey;
	}
	public void quack(){  
		 turkey.gobble();
	}
	public void fly(){   //火鸡飞行距离短,所以需要5次飞行对应鸭子的飞行
		forint i=0;i<5;i++{ 
			turkey.fly();
		}
	}

}

public class DuckTestDrive{ 
	public static void main(String[] args){ 
		MallardDuck duck = new MallardDuck();
		WildTurkey turkey = new WildTurkey();
		// 把火鸡放进火鸡适配器,让它看起来像个鸭子
		Duck turkeyAdapter = new TurkeyAdapter(turkey);
		
		System.out.println(“The turkey says…”);
		turkey.gobble();
		turkey.fiy();

		System.out.println(“\nThe Duck says…”);
		testDuck(duck);

		System.out.println(“\nThe TurkeyAdapter says…”);
		testDuck(turkeyAdapter);

	}
	static void testDuck(Duck duck){ 
		duck.quack();
		duck.fly();
	}
}

结果:
The turkey says…
Gobble gobble 
I am flying a short distance 

The Duck says…
Quack
I am flying 

The TurkeyAdapter says…
Gobble gobble
I am flying a short distance
I am flying a short distance
I am flying a short distance
I am flying a short distance
I am flying a short distance 	

四、模板模式

模板模式 :解决某类事情的步骤有些是固定的,有些是会发生变化的,那么这时候我们可以为这类事情提供一个模板代码,从而提高效率。
通过定义一个算法骨架,而将算法中的步骤延迟到子类,这样子类就可以复写这些步骤的实现来实现特定的算法。

是类的一种行为,只需要准备一个抽象类,将逻辑用具体方法和构造函数的形式来表现,后声明一些抽象方法来迫使子类必须实现其逻辑,不同的子类可以实现不同的方法,从而可以让剩余的逻辑有不同的实现。即可以定义抽象的方法,让子类实现剩余的逻辑。

分类:

抽象模版:定义的数量和类型,定义了一个抽象操作让子类实现,定义并实现了一个模版方法,该模版方法一般是一个具体方法,同时给出了顶层逻辑的框架,具体的步骤在相应的抽象操作中,具体的业务逻辑延迟到子类实现。(只有一个抽象基类)
具体模版:模版方法的数量,实现父类定义的一个或多个抽象方法,每个抽象模版的角色都可以由任意多个具体模版角色与之对应。即不是一对一,而是多对多。

例如:
1.办理银行业务:

  • 进门取号
  • 填写单据(每个客户填写的单据都不一样,因业务不同而不同)
  • 等待叫号
  • 窗口办理

2.使用场景

  • 多个子类公有的方法,并且逻辑基本相同时
  • 重要、复杂的算法,可以把核心算法设计为模版方法
  • 重构时,模版方法模式是一个经常使用的模式

3.UML结构图:

《软件体系结构与架构五大设计模式》
详情可以参考这个文档

来一个例子:


public class Coffce{ 
	void prepareRecipe(){ 
		boilWater();
		brewCoffeeGrinds(); // 冲泡咖啡
		pourInCup();
		addSugarAndMilk();
	}
	public void boilWater(){ 
		// …
	}
	public void brewCoffeeGrinds(){ 
		//…
	}
	public void pourInCup(){ 
		//…
	}
	public void addSugarAndMilk(){ 
		//…
	}
} 
public class Tea{ 
	void prepareRecipe(){ 
		boilWater();
		steepTeaBag(); //浸泡茶包
		pourInCup();
		addLemon();
	}
	public void boilWater(){ 
		// …
	}
	public void steepTeaBag(){ 
		//…
	}
	public void pourInCup(){ 
		//…
	}
	public void addLemon(){ 
		//…
	}
} 

有公共函数,该如何设计?

void prepareRecipe(){ 
		boilWater();
		brewCoffeeGrinds();
		pourInCup();
		addSugarAndMilk();
	}
void prepareRecipe(){ 
	boilWater();
	steepTeaBag();
	pourInCup();
	addLemon();
}
//生成抽象类CaffeineBeverage
Public abstract class CaffeineBeverage{  //咖啡因饮料
	final void prepareRecipe(){ 
		boilWater();
		brew();
		pourInCup();
		addCondiments();
	}
	public void boilWater(){  // 此处是否还可以改进?
		// …
	}
	abstract void brew();
	public void pourInCup(){ 
		//…
	}
	abstract void addCondiments();
} 
//实现抽象类
public class Tea extends CaffeineBeverage{ 
	public void brew(){ 
		// …
	}
	public void addCondiments(){ 
		//…
	}
} 


public class Coffee extends CaffeineBeverage{ 
	public void brew(){ 
		// …
	}
	public void addCondiments(){ 
		//…
	}
} 

如果我不想加任何添加,怎么办?

// 引入钩子方法 
Public abstract class CaffeineBeverage{  //咖啡因饮料
	public final void prepareRecipe(){ 
		boilWater();
		brew();
		pourInCup();
		if(isCustomerWantsCondiments()){ 
			 addCondiments(); 
		}	
	}
	private void boilWater(){  
		// …
	}
	protected abstract void brew();
	private void pourInCup(){ 
		//…
	}
	protected abstract void addCondiments();
	// 询问用户是否加入调料 Hook方法(钩子方法)
	// 有一个默认输出,子类决定是否挂钩(覆盖其父类的方法)
	protected boolean isCustomerWantsCondiments(){ 
		return true;
	}
} 

// 使用钩子
Public class CoffeeWithHook extends CaffeineBeverage{  //咖啡因饮料
	//…省略子类中覆盖的其他方法
	public boolean isCustomerWantsCondiments(){ 
		String answer = getUserInput();
		if(answer.toLowerCase().startsWith(“y”)){ 
			return true;
		}else{ 
			return false;
		}
	}
	private String getUserInput(){ 
		String answer = null;
		BufferedReader in = new BufferedReader
					(new InputStreamReader(System.in));
		try{ 
			answer = in.readLine();
		}catch(IOException ioe){ 
			// 做io异常处理
		}	
		if(answer == null){ return “no”;}
		return answer;
	}
} 

五、(代理、门面)

利用门面模式实现各层之间的关联与协作。

如:某层中的各个组件需要访问另一层中的不同组件,导致层与层之间出现“多对多”的关联形式

《软件体系结构与架构五大设计模式》
解决办法: 用“门面架构模式”分离“多对多”的关联。

《软件体系结构与架构五大设计模式》
门面模块与中介的区别:
门面模块:门面组件之后的各个组件之间有可能仍有相互之间的关联。
中介模块:中介模块引入后,使与中介关联的各个组件之间的关联被断绝,他们都只与中介打交道,如下面中介模式的图所示《软件体系结构与架构五大设计模式》

    原文作者:Branlen
    原文地址: https://blog.csdn.net/qq_42367749/article/details/103300301
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞