zoukankan      html  css  js  c++  java
  • 设计模式------建造者模式

    作者:haibiscuit

    时间:2019:4:9

    您的star是我不断前行的动力

    https://github.com/haibiscuit

    转载请说明出处

    前言:
    1.可以先参考我写的设计原则,总的来说最重要的是要以单一职责和抽象和接口来设计自己的类,
    其中单一原则(应该仅有一个引起它变化的原因),以及模式设计的可扩展性尤为重要。
    2.为了达到开闭原则,可以将要使用的类的路径配置在xml中,利用反射获取类的实例,另外需要配置的类在每个模式中只需要配置一种,
    例如:
    工厂模式:只需要配置工厂的实现类,目标类(即工厂创建的类在工厂的是喜爱呢类中创建)

    适配器模式:只需要配置适配器类,适配者在适配器类中创建,由适配器来操作
    另外,设计模式是为了建立不同类型的类之间的关系
    例如:
    工厂模式的工厂类和目标生成类(这里工厂模式有接口)
    适配器模式的适配者类和适配器类(这里适配器类也有接口)
    所以,上面的设计模式总结的思路很值得在实际的代码设计中使用

    目录:

    创建型:(重点推荐工厂模式和单例模式和建造者模式)
    1.工厂模式
    2.抽象工厂模式
    3.单例模式
    4.建造者模式

    结构性:(重点推荐适配器模式和代理模式)
    1.适配器模式
    2.外观模式
    3.代理模式

    行为型:(重点推荐策略模式和观察者模式)
    1.命令模式
    2.迭代器模式
    3.策略模式
    4.观察者模式
    5.中介者模式 (聊天室)

    一:工厂模式
    定义:定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个子类的实例化延迟到其子类。

    使用场景:1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。

    示例:
    1.拿日志记录器来举例
    //创建的目标类
    interface Logger{
    void writeLog();
    }
    class DatabaseLogger implements Logger{
    @Override
    public void writeLog() {
    System.out.println("数据库日志记录类");
    }
    }
    class FileLogger implements Logger{
    @Override
    public void writeLog() {
    System.out.println("文件日志记录类");
    }
    }

    //定义工厂类
    interface LoggerFactory{
    Logger createLogger();
    }
    class DatabaseLoggerFactory implements LoggerFactory{
    @Override
    public Logger createLogger() {
    //创建数据库日志记录器类
    Logger logger = new DatabaseLogger();
    return logger;
    }
    }
    class FileLoggerFactory implements LoggerFactory{
    @Override
    public Logger createLogger() {
    //创建数据库日志记录器类
    Logger logger = new FileLogger();
    return logger;
    }
    }

    //测试类
    class Test{
    public static void main(String []args){
    LoggerFactory factory;
    Logger logger;

    factory = new FileLoggerFactory(); //可引入配置文件实现(原理:从配置文件中获取类的路径,再通过反射类创建工厂类,为了实现开闭原则,可以在我的适配器模式中看到思路)
    logger = factory.createLogger();

    logger.writeLog();
    }
    }
    总结:
    1.每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

    二:抽象工厂模式
    定义:提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。
    使用场景:1、QQ 换皮肤,一整套一起换。 2、生成不同操作系统的程序。
    例如:
    创建Spring风格的浅绿色的按钮,
    绿色边框的文本框,
    绿色边框的组合框
    创建Summer风格的浅蓝色的按钮,
    蓝色边框的文本框,
    蓝色边框的组合框
    示例:
    //目标创建类
    interface Button{
    void display();
    }
    class SpringButton implements Button{
    @Override
    public void display(){
    System.out.println("Spring风格Button");
    }
    }
    class SummerButton implements Button{
    @Overrider
    public void display(){
    System.out.println("Spring风格Button");
    }
    }
    //文本框、组合框的实现和Button一样
    //以下是抽象工厂类的实现
    interface SkinFactory{
    Button createButton();
    TextField createTextField();
    comboBox createComboBox();
    }
    //Spring风格的工厂实现类
    class SpringSkinFactory implements SkinFactory{
    @Override
    public Button createButton(){
    return new SpringButton();
    }
    @Override
    public TextField createTextField(){
    return new SpringTextField();
    }
    @Override
    public comboBox createComboBox(){
    return new SpringComboBox();
    }
    }
    //Summer风格的工厂实现类
    class SpringSkinFactory implements SkinFactory{
    @Override
    public Button createButton(){
    return new SummerButton();
    }
    @Override
    public TextField createTextField(){
    return new SummerTextField();
    }
    @Override
    public comboBox createComboBox(){
    return new SummerComboBox();
    }
    }

    class Test{ //为了实现开闭原则,可以将工厂的具体实现类的路径写在xml中,利用反射来生成对象,这里只是简单的new一下,具体参考适配器中的示例
    public static void main(String []args){
    SkinFactory factory;
    Button button;

    factory = new SpringSkinFactory();
    button = factory.createButton();


    button.display();
    }

    }
    总结:
    1.产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
    2.但是这里的可扩展性就有点烦,比如以上实现了Summer和Spring风格的Button,TextField,ComboBox,
    如果我想实现Winter风格的,需要分别继承Button,TextField,ComboBox实现新的类,同样需要继承SkinFactory工厂类,实现WinterSkinFactory工厂实现类。

    三:单例模式
    定义:
    保证类只有一个实例。
    优势:
    1.在Spring中默认的创建类型为单例
    2.在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
    3.避免对资源的多重占用(比如写文件操作)
    示例:

    //示例一
    // Effective Java 第一版推荐写法,主要是final可以缓存实例
    class Singleton {
    private static class SingletonHolder {

    private static final Singleton INSTANCE = new Singleton();

    }
    private Singleton (){}

    public static final Singleton getInstance() {

    return SingletonHolder.INSTANCE;

    }
    }

    //最好的写法
    //这里主要好的原因是volatile,可以避免多线程带来的不安全(当然概率很低)
    class Single4 {

    private static volatile Single4 instance;

    private Single4() {}

    public static Single4 getInstance() {

    if (instance == null) {

    synchronized (Single4.class) {

    if (instance == null) {

    instance = new Single4();

    }

    }

    }

    return instance;

    }

    }
    总结:
    Spring默认的创建对象的姿势就是单例模式,但是使用单例模式需要着重考虑的是资源共享的线程安全问题。

    四:适配器模式
    定义:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

    使用场景:
    1、美国电器 110V,中国 220V,就要有一个适配器将 110V 转化为 220V。 2、JAVA JDK 1.1 提供了 Enumeration 接口,而在 1.2 中提供了 Iterator 接口,想要使用 1.2 的 JDK,则要将以前系统的 Enumeration 接口转化为 Iterator 接口,这时就需要适配器模式
    示例:以下使用c#的方式演示(看结构和思路,主要为了演示开闭原则的实现)
    //抽象成绩操作类,充当目标接口
    interface ScoreOperation{
    int [] Sort(int []array); //成绩排序
    int Search(int [] array,int key);
    }

    //快速排序类,充当适配者
    class QuickSortClass{
    public int[] QuickSort(int []array){
    //内部函数的调用,考虑的很nice
    Sort(array,0,array.Lenrth-1);
    return array;
    }
    //这里是具体操作的函数,真的很nice
    public void Sort(int []array,int p,int r){

    }
    }
    //二分查找类,充当适配者
    class BinarySearchClass{
    //这里参数和接口中的参数是相同的
    public int BinarySearch(int []array,int key){
    return 0;
    }
    }
    //操作适配者,充当适配器
    class OperationAdapter:ScoreOperation{
    private QuickSortClass sortObj;
    private BinarySearchClass searchObj;

    public OperationAdapter(){
    sortObj = new QuickSortClass();
    searchObj = new BinarySearchClass();
    }

    @Override
    public int [] Sort(int [] array){
    sortObj.QuickSort(array); //调用适配者类的排序方法
    }
    @Override
    public int Search(int []array,int key){
    //调用适配者类BinarySearchClass
    return searchObj.BinarySearch(array,key);
    }
    }
    //配置App.config,演示开闭原则
    <?xml version = "1.0" encoding = "utf-8"?>
    <configuration>
    <appSetting>
    <add key = "adapter" value="AdapterSample.Operationdapter"
    <appSetting>
    </configuration>

    class Test{ //这里使用的是c#啦
    static void Main(string){
    ScoreOperation operation;

    //读取配置文件
    string adapterType = ConfigurationManager.AppSettings("adapter");
    //反射生成实例
    operation = (ScoreOperation) Assembly.Load("AdapterSample").CreateInstance(adapterType);


    int []scores = {84,76,50,69};
    int []result;
    int score;

    result = operation.Sort(scores); //排序
    operation.Search(result,90); //查找成绩
    }
    }


    总结:
    1.要搞清楚适配器和适配者之间的关系
    2.另外,注意这里的开闭原则的实现思路
    3.可以看到,适配器和适配者,都有相同的行为,所以可以实现适配器来操作适配者。

    五:代理模式
    1.代理模式分静态代理,动态代理,cglib代理方式
    这里只分析静态代理和动态代理
    2.Spring中的Aop的底层原理就是使用的代理方式

    示例: //直接上代码,代码就是最好的解释
    (1).静态代理
    //接口
    public interface IUserDao {
    void save();
    }
    //接口实现,目标对象
    public class UserDao implements IUserDao {
    @Override
    public void save() {
    System.out.println("----已经保存数据!----");
    }
    }
    //代理类
    public class UserDaoProxy implements IUserDao{
    //接收保存目标对象
    private IUserDao target;
    public UserDaoProxy(IUserDao target){
    this.target=target;
    }

    public void save() {
    System.out.println("开始事务...");
    target.save();//执行目标对象的方法
    System.out.println("提交事务...");
    }
    }
    //测试类
    class Test{
    public static void main(String []args){
    //目标对象
    UserDao target = new UserDao();

    //代理对象,把目标对象传给代理对象,建立代理关系
    UserDaoProxy proxy = new UserDaoProxy(target);

    proxy.save();//执行的是代理的方法
    }
    }

    (2).动态代理
    /**
    * 创建动态代理对象
    * 动态代理不需要实现接口,但是需要指定接口类型
    */
    public class ProxyFactory{

    //维护一个目标对象
    private Object target;
    public ProxyFactory(Object target){
    this.target=target;
    }

    //给目标对象生成代理对象
    public Object getProxyInstance(){
    return Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("开始事务2");
    //运用反射执行目标对象方法
    Object returnValue = method.invoke(target, args);
    System.out.println("提交事务2");
    return returnValue;
    }
    }
    );
    }

    }

    //测试类
    public class Test {
    public static void main(String[] args) {
    // 目标对象
    IUserDao target = new UserDao();

    // 给目标对象,创建代理对象
    IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();

    // 执行方法
    proxy.save();
    }
    }
    六:策略模式
    定义:定义一系列算法,将每一个算法封装起来,并让他们可以相互替换。
    策略模式让算法可以独立于使用它的客户变化。

    应用实例:(为了方便理解应用场景,举一个简单的例子)
    旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略

    使用场景:
    1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。

    2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

    示例:
    //接口
    public interface Strategy {
    public int doOperation(int num1, int num2);
    }
    //实现类
    public class OperationAdd implements Strategy{
    @Override
    public int doOperation(int num1, int num2) {
    return num1 + num2;
    }
    }

    public class OperationSubstract 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;
    }
    }

    //代理类
    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);
    }
    }

    //测试类
    class Test{
    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 OperationSubstract());
    System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

    context = new Context(new OperationMultiply());
    System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
    }
    }

    总结:
    1.策略模式很像代理模式
    2.策略模式就是为了根据客户需要,动态的改变行为

    七:命令模式(指令模式)
    定义:
    将一个请求封装成一个对象,从而让你可以用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销操作
    应用实例:
    可以参考Hystrix的实现方式,就是采用命令模式实现
    使用场景:
    可以将接受者的操作封装在命令类中,将命令类封装在调用者中,直接使用调用者,即可完成对操作者的操作,
    可以解决解耦和的作用
    public class CommandMode {
    public static void main(String []args){
    Command command; //命令对象
    command = new ExitCommand();

    FunctionButton functionButton = new FunctionButton(command);
    functionButton.click(); //即在调用者调用时,命令对象操作接受者
    }
    }
    /**
    * 调用者
    */
    class FunctionButton{
    private Command command;
    public FunctionButton(Command command){ //构造注入
    this.command = command;
    }
    public Command getCommand() {
    return command;
    }
    public void setCommand(Command command) {
    this.command = command;
    }
    public void click(){ //调用命令类的方法
    command.Execute();
    }
    }
    /**
    * 抽象命令类
    */
    abstract class Command{
    abstract void Execute();
    }
    class ExitCommand extends Command{
    private SystemExitClass seObj;
    public ExitCommand(){
    this.seObj = new SystemExitClass();
    }
    @Override
    void Execute() {
    seObj.Exit();
    }

    }

    /**
    * 接收者
    */
    class SystemExitClass{ //退出系统模拟实现类
    public void Exit(){
    System.out.println("退出系统!");
    }
    }
    总结:
    命令模式是一种行为型模式,首先要了解行为的执行模型:
    调用者->命令类->接受者
    也就是,调用者间接地通过命令类来操作接受者,
    这里可以通过拓展命令类的方式来完成对接受者的操作(例如上面的实例中,可以通过点击按钮(调用者)实现其他对接受者的功能,例如退出系统(已实现),打开文档,可以将打开文档的行为封装在接受者中,通过拓展命令类的方式(这里可以实现一个OpenDocuCommand的命令类)来完成对接收者的操作。)


    八:建造者模式
    定义:
    将一个复杂对象的构建与它的表示(可以理解为属性)分离,使得同样的构建过程可以创建不同的表示。

    包含对象:
    1.产品Product(也就是上面讲的复杂对象)
    2.抽象建造者Builder(复杂对象的构建过程在具体建造者中)
    3.具体建造者ConcreteBuilder
    4.指挥者Director(可以理解为Controller)

    应用实例:
    1.例如需要构建一个游戏角色类Actor,该类中包含角色类型,性别,面容,服装,发型,
    角色肯定不是一类角色,现在需要解决的是如何构建不同的角色类,而构建者模式就可以解决这一问题

    使用场景:
    下面通过儿童套餐类的编写来让大家体验构建者模式的优势.
    /**
    *
    * @ClassName: SetMeal
    * @Description: 儿童套餐测试类
    * @author: haibiscuit
    * @date: 2019年4月9日 下午1:22:43
    * @version 1.8.0
    * @param
    *
    */
    public class SetMeal {
    public static void main(String args[]){
    MealBuilder mealBuilder;


    mealBuilder = new SweetMealBuilder(); //香甜口味的套餐

    BabyMealController babyMealController = new BabyMealController();
    BabyMeal babyMeal = babyMealController.Construct(mealBuilder);

    System.out.println(babyMeal.getPie());
    System.out.println(babyMeal.getAppleIce());
    System.out.println(babyMeal.getNoodles());

    mealBuilder = new PungentMealBuilder(); //麻辣口味的套餐
    babyMeal = babyMealController.Construct(mealBuilder);

    System.out.println(babyMeal.getPie());
    System.out.println(babyMeal.getAppleIce());
    System.out.println(babyMeal.getNoodles());


    }
    }
    /**
    *
    * @ClassName: BabyMeal
    * @Description: 儿童套餐类
    * @author: haibiscuit
    * @date: 2019年4月9日 下午1:26:51
    * @version 1.8.0
    * @param
    *
    */
    class BabyMeal{
    private String Pie; //馅饼
    private String AppleIce; //苹果冰淇淋
    private String Noodles; //儿童面食
    public String getPie() {
    return Pie;
    }
    public void setPie(String pie) {
    Pie = pie;
    }
    public String getAppleIce() {
    return AppleIce;
    }
    public void setAppleIce(String appleIce) {
    AppleIce = appleIce;
    }
    public String getNoodles() {
    return Noodles;
    }
    public void setNoodles(String noodles) {
    Noodles = noodles;
    }
    }
    /**
    *
    * @ClassName: MealBuilder
    * @Description: 抽象套餐的建造者
    * @author: haibiscuit
    * @date: 2019年4月9日 下午1:28:32
    * @version 1.8.0
    * @param
    *
    */
    abstract class MealBuilder{
    protected BabyMeal babyMeal = new BabyMeal();


    public abstract void buildPie();
    public abstract void buildAppleIce();
    public abstract void buildNoodles();

    public BabyMeal CreateBabyMeal(){
    return babyMeal;
    }
    }
    /**
    *
    * @ClassName: SweetMealBuilder
    * @Description: 具体建造者类
    * @author: haibiscuit
    * @date: 2019年4月9日 下午1:45:52
    * @version 1.8.0
    * @param
    *
    */
    class SweetMealBuilder extends MealBuilder{

    @Override
    public void buildPie() {
    babyMeal.setPie("香甜Pie");
    }

    @Override
    public void buildAppleIce() {
    babyMeal.setAppleIce("香甜Ice");

    }

    @Override
    public void buildNoodles() {
    babyMeal.setNoodles("香甜Noodles");
    }
    }
    class PungentMealBuilder extends MealBuilder{
    @Override
    public void buildPie() {
    babyMeal.setPie("麻辣Pie");
    }
    @Override
    public void buildAppleIce() {
    babyMeal.setAppleIce("麻辣Ice");
    }
    @Override
    public void buildNoodles() {
    babyMeal.setNoodles("麻辣Noodles");
    }
    }

    /**
    *
    * @ClassName: BabyMealController
    * @Description: 生产儿童控制器的控制器
    * @author: haibiscut
    * @date: 2019年4月9日 下午1:52:07
    * @version 1.8.0
    * @param
    *
    */

    class BabyMealController{
    public BabyMeal Construct(MealBuilder mealBuilder){
    BabyMeal babyMeal;
    mealBuilder.buildAppleIce();
    mealBuilder.buildNoodles();
    mealBuilder.buildPie();

    babyMeal = mealBuilder.CreateBabyMeal();
    return babyMeal;
    }
    }

    总结:
    在使用模式的时候关键的概念是变与不变,而建造者模式只需要继承抽象的Builder类就可以达到模式的扩展功能,可以扩展不同的Actor角色对象,
    相比于创建行动额模式:工厂模式,抽象工厂模式,构建者模式要相对的简单和灵活,所以笔者非常推荐各位来使用这一设计模式.
    另外,构建者模式对象的关系模型大概是:
    Product->Builder->Controller(最终由Controller来完成Product属性的构建)


  • 相关阅读:
    HttpWebRequest 改为 HttpClient 踩坑记-请求头设置
    [k8s]docker calico网络&docker cluster-store
    [k8s]jenkins部署在k8s集群
    [k8s]zookeeper集群在k8s的搭建(statefulset模式)-pod的调度
    [svc]cisco ipsec使用证书认证
    [svc]数字证书基础知识
    [svc]logstash和filebeat之间ssl加密
    [svc]cfssl模拟https站点-探究浏览器如何校验证书
    [svc] cisco router as ca server
    [svc]对称加密/非对称加密细枝末节-如何做到数据传输的authentication/data integrity/confidentiality(私密)
  • 原文地址:https://www.cnblogs.com/haibiscuit/p/10678105.html
Copyright © 2011-2022 走看看