山东大学面向对象技术——设计原则、工厂模式、单例模式

目录

前言

设计模式和原则作用

面向对象的设计原则

开闭原则OCP: Open-Closed Principle 

题目举例 

里氏代换原则LSP: Liskov Subtitution

依赖倒置原则DIP: Dependency Inversion Principle

接口隔离原则ISP: Interface Segregation Principle

 组合优先原则CRP: Composition Reuse Principle

迪米特法则 LOD

单一职责原则SRP

 面向对象的设计模式

单例模式

工厂模式

简单工厂

工厂方法 

抽象工厂 

总结 


前言

写在前面:本文是主要针对山东大学软件学院面向对象技术课程设计模式和设计原则部分的总结,包括老师课上讲解的主要设计模式,类图和往年回忆考题中的设计类题目和自己写的代码(都是正确可运行的)

设计模式和原则作用

目标:提高面向对象设计复用性的设计原则。满足可拓展性灵活性可插入性

复用:尽量从具体模块中,抽象出抽象模块;抽象模块独立于具体模块设计 

面向对象的设计原则

  1. 开闭原则OCP: Open-Closed Principle
  2. 里氏代换原则LSP: Liskov Subtitution
  3. 依赖倒置原则DIP: Dependency Inversion Principle
  4. 接口隔离原则ISP: Interface Segregation Principle
  5. 组合复用原则CRP: Composition Reuse Principle
  6. 迪米特法则LoD: Law of Demeter
  7. 单一职责原则SRP

开闭原则OCP: Open-Closed Principle 

定义:软件实体对拓展是开发的,但是对修改是关闭的。试图设计永远不需要改变的模块(关键在于抽象层次结构的设计,抽象模块是不需要改变的,只要不停在抽象模块下面增加具体模块即可)

目标:尝试在软件系统中减少不满足OCP原则的模块数量(尽量增加抽象层次,让具体层次和抽象层次绑定)。同时做到解耦(解耦指的是减少模块或类之间的依赖,使得一个模块的变更不会影响到其他模块。通过解耦,可以实现更高的模块化和灵活性,使得系统更容易扩展和维护)

题目举例 

题目一:

  • 给定某几种商品,要求商品可以拓展,商品要求可以设置价格,并且可能设置商品折扣
class Part{protected double price;public void setPrice(double price){this.price=price;}public double getPrice(){return price;}
}
class Memory extends Part{}
class Disk extends Part{}
public class Test {public static void main(String[] args){Part p1=new Memory();p1.setPrice(599);Part p2=new Disk();p1.setPrice(499);Part[] part={p1,p2};System.out.println(sumPrice(part));}public static double sumPrice(Part[] parts){double total = 0.0;for (int i=0;i<parts.length;i++){total += parts[i].getPrice();}return total;}
}

分析代码:

如果此时要给商品促销,那么我们就需要重新修改函数sumPrice,这就不符合OCP原则。因此,要提取抽象层次,抽象出一个类pricePolicy表示促销策略(让具体促销价格计算放在每个商品内部解决)。然后在sumPrice中对所有商品调用同样的getPrice即可

修改代码如下:

class Part{protected double price;private pricePolicy pricePolicy;public void setPrice(double price){this.price=price;}public double getPrice(){if(pricePolicy == null)return price;elsereturn pricePolicy.getPrice(price);}
}
class pricePolicy{public double getPrice(double basePrice){return basePrice;}
}
class sale extends pricePolicy{private double discount;public void setDiscount(double discount){this.discount = discount;}public double getPrice(double basePrice){return basePrice * discount;}
}
class Memory extends Part{}
class Disk extends Part{}
public class Test {public static void main(String[] args){Part p1=new Memory();p1.setPrice(599);Part p2=new Disk();p1.setPrice(499);Part[] part={p1,p2};System.out.println(sumPrice(part));}public static double sumPrice(Part[] parts){double total = 0.0;for (int i=0;i<parts.length;i++){total += parts[i].getPrice();}return total;}
}

抽象出了pricePolicy类,然后在每个商品类中都加入pricePolicy属性这样每个商品都有独立的价格政策。每个商品具体的价格政策sale又是独立的类继承自pricePolicy,如此,新增价格政策时不是修改pricePolicy而是新增一个具体类,放到这个抽象类中,抽象类用来返回具体类中计算的最终价格 

方法:

不符合OCP原则,则想办法抽象出新的层次,在抽象层次去调用,从而提高复用率 

题目二:

  • 绘制图形,并提供展示方法,要求每增加一个新的图形,都要尽量满足OCP原则
class Shape{}
class Circle extends Shape {void drawCircle(){System.out.println("I am drawing a circle in switch!"); }
}
class Square extends Shape{void drawSquare(){System.out.println("I am drawing a square in switch!"); }
}import java.util.ArrayList;
import java.util.List;
public class Test{    public static void main(String[] args){        List<Shape> shapeList=new ArrayList<Shape>();        Circle c=new Circle();        Square s=new Square();        shapeList.add(c);        shapeList.add(s);        switchDraw(shapeList);   }    static void switchDraw(List shapeList){        for(int i=0;i<shapeList.size();i++){  if(shapeList.get(i) instanceof Circle){ Circle circle= (Circle)shapeList.get(i);                 	 circle.drawCircle();           }else if(shapeList.get(i) instanceof Square){ Square square= (Square)shapeList.get(i);     square.drawSquare();           }       }   }
}
// 这种实现如果在图形形状多的情况下循环分支数量会很大,不推荐

分析代码:

如果现在要增加一个图形形状,那么我们需要修改switchDraw画图函数,一旦图形形状增多后,这个分支将非常长,并且修改函数不符合OCP原则。所以要提取抽象层次,把画图提取为draw函数,从而使得switchDraw函数转化为draw函数。

因此具体如何draw就不由main决定而是由每一个类自己内部决定。将每个图形继承自shape类,shape类要有一个draw抽象函数,这个抽象函数由每个具体类去具体实现。

修改代码如下:

import java.util.ArrayList;
abstract class shape {abstract void draw();
}
class circle extends shape{void draw(){System.out.println("a circle is drawing");}
}
class square extends shape{void draw(){System.out.println("a square is drawing");}
}
public class Test{public static void main(String[] args) {ArrayList<shape> arrayList = new ArrayList<shape>();shape circle = new circle();shape square = new square();arrayList.add(circle);arrayList.add(square);for(int i=0;i<arrayList.size();i++){arrayList.get(i).draw();}}
}

里氏代换原则LSP: Liskov Subtitution

定义:如果对每一个类型为T_1的对象o_1,都有类型为T_2的对象o_2,使得T_1定义的所有程序P在所有的对象o_1都换成o_2​时,程序P的行为没有变化,那么类型T_2是类型T_1的子类型 (可替换原则)

简单来说:父类适用的地方替换成子类一样适用 

里氏替换原则是实现开闭原则的重要方法之一。如果一个程序不符合LSP,那么程序用子类替换父类,我们就需要修改程序

实现LSP的方法:

在程序中尽量用基类类型定义对象,在具体运行实现的时候再用子类类型,并用子类类型去替换父类对象 

在语法上,在父类类型中使用子类对象肯定是没有错误的,那么为什么有的程序不满足LSP原则呢?既然不是语法错误,那么一定是程序逻辑上的问题,也就是说有的子类在逻辑上就不能代替父类使用(逻辑上并不继承)

例如:鸟一般都会飞行,如燕子的飞行速度大概是每小时 120 千米。但是新西兰的几维鸟由于翅膀退化无法飞行。假如要设计一个实例,计算这两种鸟飞行 300 千米要花费的时间。显然,拿燕子来测试这段代码,结果正确,能计算出所需要的时间;但拿几维鸟来测试,结果会发生“除零异常”或是“无穷大”,明显不符合预期

生活中存在一类事物在认知上是存在继承关系,但是在行为上并不存在继承关系。这一类事物用继承来实现便不符合LSP(行为上不存在继承关系——>子类父类行为上不可复用——>子类父类函数不可复用——>复用时函数需要修改——>不符合OCP和LSP)

例如:

class Bird {    double flySpeed;   public void setSpeed(double speed) {       flySpeed = speed;   }    public double getFlyTime(double distance) {   return (distance / flySpeed);  }
}
//几维鸟类
class BrownKiwi extends Bird {    public void setSpeed(double speed) {       flySpeed = 0;   }
}
public class Test {   public static void main(String[] args) {        Bird bird1 = new Swallow();        Bird bird2 = new BrownKiwi();   bird1.setSpeed(120);        bird2.setSpeed(120);        System.out.println("如果飞行300公里:");        try {            System.out.println("燕子将飞行" + bird1.getFlyTime(300) + "小时.");System.out.println("几维鸟将飞行" + bird2.getFlyTime(300) + "小时。");       } catch (Exception err) {  System.out.println("发生错误了!");      }  }
}
// 这显然是不满足里氏代换原则的

遵守的方法:

一旦两个事物在行为上不严格存在继承关系,那么便定义为两个类,不使用继承关系 


依赖倒置原则DIP: Dependency Inversion Principle

定义:

高层模块不应该依赖底层模块,二者都应该依赖其抽象;抽象不应该依赖细节;而细节应该依赖抽象(所有细节模块都依赖抽象模块,类之间的联系全靠抽象模块)

必要性:

假如现在有一个司机类又有一个汽车类,司机要开汽车那么汽车类和司机类一定要产生依赖关系。假如我们将一个具体的司机和一个具体的车联系起来,如果我们增加了一辆新的车便要重新添加联系,这将造成麻烦,并且模块之间的耦合性太强

实现核心:

利用抽象类/接口来提取抽象层次,将模块之间的联系转化为抽象层次的联系

两种实现方式:

 a.接口声明实现依赖对象

public interface ICar{public void run();
}
public interface IDriver {public void drive(ICar car);
}
public class Benz implements ICar {public void run(){System.out.println("奔驰汽车开始运行...");}
}
public class Driver implements IDriver {public void drive(ICar car){car.run();}
}
public class Test {public static void main(String[] args){IDriver driver1=new Driver();ICar benz=new Benz();driver1.drive(benz);}
}

b. 构造函数注入实现依赖对象

public interface ICar{public void run();
}
public interface IDriver {public void drive();
}
public class Benz implements ICar {public void run(){System.out.println("奔驰汽车开始运行...");}
}
public class Driver implements IDriver {private ICar car;public Driver(ICar benz) {this.car=car;}public void drive(){this.car.run();}
}
public class Test {public static void main(String[] args){ICar benz=new Benz();IDriver driver1=new Driver(benz);driver1.drive();}
}

 c.Setter方法传递依赖对象:

public interface ICar{public void run();
}
public interface IDriver {public void drive();
}
public class Benz implements ICar {public void run(){System.out.println("奔驰汽车开始运行...");}
}
public class Driver implements IDriver {private ICar car;public void setCar(ICar benz) {this.car=car;}public void drive(){this.car.run();}
}
public class Test {public static void main(String[] args){ICar benz=new Benz();IDriver driver1=new Driver();driver1.setCar(benz);driver1.drive();}
}

本人最喜欢抽象接口实现依赖 ,使用这个更符合上图中的类关系图,真正的依赖绑定是在接口声明中进行

 理解关键点:

1、“正置”:依赖正置就是类间的依赖是实实在在地实现类间的依赖,也就是面向实现编程,这也是正常人的思维方式,我要开奔驰车就依赖奔驰车,我要使用笔记本电脑就直接依赖笔记本电脑

2、而编写程序需要的是对现实世界的事物进行抽象,抽象的结果就是有了抽象类和接口,然后我们根据系统设计的需要产生了抽象间的依赖,代替了人们传统思维中的事物间的依赖,“倒置”就是从这里产生的。


接口隔离原则ISP: Interface Segregation Principle

定义:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口

目标:接口功能明确;一个接口表示一个角色;接口尽量少且专一

接口分工明确;接口职能明确;接口功能交集少


 组合优先原则CRP: Composition Reuse Principle

思想:将代码易变化的部分封装起来,将其和代码不变的部分独立开来(将一个物品中多变化的部分独立变为一个模块,利用组合复用来搭建原本的物品);这个分开要求二者本身是可独立的模块

目标:实现可变性和不可变性的分离,将可变因素映射为同一个抽象类的不同子类。(将可独立模块分隔开,利用组合来复用而不是继承来实现)

示例:蜡笔和毛笔的区别。蜡笔的型号和颜色在出厂时就已经确定,二者不是可独立模块,但是毛笔和型号和颜色是可分离和可变化的,故可以采用桥梁模式。

同时在毛笔中按照组合复用原则,封装可变部分,利用桥梁模型进行设计,可以避免继承带来的缺点(破坏封装、子父类耦合、继承是静态的不能改变)又保留继承的优点(子类能够拓展父类的实现,而不用修改父类,符合开闭原则)

核心部分:在于独立的两个模块(Abstraction和Implementor依靠关联和聚合达成继承的效果,共同组成一个物体) 


迪米特法则 LOD

定义:

一个对象应该对其他对象保持最少的了解。

如果两个类不必彼此通信,那么这两个类就不应当发生直接的相互作用。如果其中的一个类需要调用另一个类的某一个方法的话,可通过第三者实现调用(朋友圈)

原则出现的原因:

类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。因此想要尽量减少各个类之间的关系


单一职责原则SRP

定义:一个类只负责一项职责。此原则的核心就是解耦增强内聚性

原则出现的原因:

类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。因此,遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。

举例:

假如存在一个类描述动物的呼吸:

class Animal{public void breathe(String animal){System.out.println(animal+"呼吸空气");}
}
public class Client{public static void main(String[] args){Animal animal = new Animal();animal.breathe("牛");animal.breathe("羊");animal.breathe("猪");}
}

此时要增加一个呼吸水的功能,按照单一职责原则修改如下:

class Terrestrial{public void breathe(String animal){System.out.println(animal+"呼吸空气");}
}
class Aquatic{public void breathe(String animal){System.out.println(animal+"呼吸水");}
}public class Client{public static void main(String[] args){Terrestrial terrestrial = new Terrestrial();terrestrial.breathe("牛");terrestrial.breathe("羊");terrestrial.breathe("猪");Aquatic aquatic = new Aquatic();aquatic.breathe("鱼");
}}

如果不遵循单一职责原则,代码如下:

class Animal{public void breathe(String animal){if("鱼".equals(animal)){System.out.println(animal+"呼吸水");}else{System.out.println(animal+"呼吸空气");}}
}public class Client{public static void main(String[] args){Animal animal = new Animal();animal.breathe("牛");animal.breathe("羊");animal.breathe("猪");animal.breathe("鱼");}
}

可以看到,这种修改方式要简单的多。但是却存在着隐患:有一天需要将鱼分为呼吸淡水的鱼和呼吸海水的鱼,则又需要修改Animal类的breathe方法,而对原有代码的修改会对调用“猪”“牛”“羊”等相关功能带来风险,也许某一天你会发现程序运行的结果变为“牛呼吸水”了。这种修改方式直接在代码级别上违背了单一职责原则,虽然修改起来最简单,但隐患却是最大的。


 面向对象的设计模式

设计模式的分类

  • 创建型:工厂模式、单例模式
  • 结构型:桥梁模式、适配器模式、装饰者模式
  • 行为型:观察者、责任链、策略模式

单例模式

定义:一个类只有一个实例,且该类能够自行创建这个实例的(不用手动new)

特点:

  1. 单例类只有一个实例对象
  2. 该单例对象必须由单例类自行创建
  3. 单例类对外提供一个访问该单例对象的全局访问点

优点:

  1. 单例模式可以保证每个单例类内存中只有一个实例,减少内存开销。避免资源重复浪费
  2. 单例对象设置全局访问点,优化对其的访问

缺点:

  1. 单例模式没有接口,拓展困难
  2. 单例模式的功能只能写在一个类中,如果设计的不合理,将违背单一职责原则(要求一个类/方法/接口只实现一个功能)

类图:

单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例。饿汉式单例在单例类被加载时候,就实例化一个对象交给自己的引用;而懒汉式在调用取得实例方法的时候才会实例化对象。
代码如下: 

饿汉式单例

public class Singleton {private Singleton(){}//private保证外界不能获取,控制实例唯一性private static Singleton singleton=new Singleton();//static保证延迟实例化(在需要时才创建)public static Singleton getInstance(){return singleton;}//全局可访问性public static void main(String[] args) {for(int i = 0; i <2; i++){Singleton obj = Singleton.getInstance();System.out.println(obj);   //获得对象,打印}}
}

 懒汉式单例

public class Singleton {private Singleton(){}//private保证外界不能获取,控制实例唯一性private static Singleton singleton;//懒汉式并不直接创建实例对象,在调用getInstance才生成public static Singleton getInstance(){if(singleton==null){singleton = new Singleton();}return singleton;}//全局可访问性public static void main(String[] args) {for(int i = 0; i <2; i++){Singleton obj = Singleton.getInstance();System.out.println(obj);   //获得对象,打印}}
}

处理多线程

public class Singleton {private static Singleton uniqueInstance;// other useful instance variables hereprivate Singleton() {}public static synchronized Singleton getInstance() {if (uniqueInstance == null) {uniqueInstance = new Singleton();}return uniqueInstance;}// other useful methods here
}

工厂模式

  • 简单工厂:一个工厂完成所有产品的生产
  • 工厂方法:将不同产品分给不同工厂去生产(分工)
  • 抽象工厂:对分工后的工厂提取一个抽象层次

简单工厂

定义:产品实现接口;工厂生产产品

类图:

角色对象:

工厂类(Creator)角色:该角色是工厂方法模式的核心,含有与应用紧密相关的商业逻辑。工厂类在客户端的直接调用下创建产品对象,它往往由一个具体类实现。

抽象产品(Product)角色:担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。抽象产品角色可以用接口或者抽象类实现。

具体产品(Concrete Product)角色工厂方法模式所创建的任何对象都是这个角色的实例,具体产品角色由一个具体类实现。

代码如下:

抽象产品(水果接口)

public interface FruitIF { 
void grow(); 
void harvest(); 
void plant(); 
} 

 具体产品(具体水果)

public class Apple implements FruitIF {private int treeAge;@Overridepublic void grow() {System.out.println("Apple is growing...");}@Overridepublic void harvest() {System.out.println("Apple has been harvested.");}@Overridepublic void plant() {System.out.println("Apple has been planted.");}public int getTreeAge() {return treeAge;}public void setTreeAge(int treeAge) {this.treeAge = treeAge;}
}
public class Strawberry implements FruitIF {@Overridepublic void grow() {System.out.println("Strawberry is growing...");}@Overridepublic void harvest() {System.out.println("Strawberry has been harvested.");}@Overridepublic void plant() {System.out.println("Strawberry has been planted.");}
}
public class Grape implements FruitIF {@Overridepublic void grow() {System.out.println("Grape is growing...");}@Overridepublic void harvest() {System.out.println("Grape has been harvested.");}@Overridepublic void plant() {System.out.println("Grape has been planted.");}
}

工厂(水果代理商)

public class FruitGardener {public FruitIF factory(String which) throws BadFruitException {if (which.equalsIgnoreCase("apple")) {return new Apple();} else if (which.equalsIgnoreCase("strawberry")) {return new Strawberry();} else if (which.equalsIgnoreCase("grape")) {return new Grape();} else {throw new BadFruitException("Bad fruit request");}}public static void main(String[] args) {FruitGardener gardener = new FruitGardener();try {FruitIF apple = gardener.factory("apple");apple.grow();apple.harvest();apple.plant();FruitIF strawberry = gardener.factory("strawberry");strawberry.grow();strawberry.harvest();strawberry.plant();FruitIF grape = gardener.factory("grape");grape.grow();grape.harvest();grape.plant();} catch (BadFruitException e) {System.out.println(e.getMessage());}}
}

优缺点: 

工厂方法 

定义:在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的产品对象,而是针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构

本质就是分解+抽象层次抽取:为不同的产品分配上不同的工厂,并为这些工厂抽取抽象工厂

角色:

Product
  定义工厂方法所创建的对象的接口,也就是实际需要使用的对象的接口。

ConcreteProduct
   具体的 Product 接口的实现对象。
Facotry
   创建器,声明工厂方法,工厂方法通常会返回一个 Product 类型的实例对象,而且多是抽象方法。也可以在 Factory 里面提供工厂方法的默认实现,让工厂方法返回一个缺省的 Product 类型的实例对象。
ConcreteFacotry
    具体的创建器对象,(覆盖)实现 AbstractFacotry 定义的工厂方法,返回具体的 Product 实例。

类图: 

实现: 

 抽象产品(Logger)

//日志记录器接口:
//抽象产品  interface Logger {      public void writeLog();   
}     //数据库日志记录器:具体产品   

具体产品

class DatabaseLogger implements Logger {   public void writeLog() {  System.out.println("数据库日志记录");   }   }     //文件日志记录器:具体产品  class FileLogger implements Logger {public void writeLog() {  System.out.println("文件日志记录。");     }   }    

 抽象工厂

 interface LoggerFactory {      public Logger createLogger();   }      //数据库日志记录器工厂类:具体工厂  

具体工厂 

class DatabaseLoggerFactory implements LoggerFactory {      public Logger createLogger() {   //连接数据库,代码省略 ;创建数据库日志记录器对象               Logger logger = new DatabaseLogger(); //初始化数据库日志记录器代码省略                  return logger;       }      }     //文件日志记录器工厂类:具体工厂   
class FileLoggerFactory implements LoggerFactory {       public Logger createLogger() {         //创建文件日志记录器对象                     Logger logger = new FileLogger();               //创建文件,代码省略             return logger;     }      }

客户端

class Client {  public static void main(String args[]) {  LoggerFactory factory;  Logger logger;
//可引入配置文件实现    factory = new FileLoggerFactory(); logger = factory.createLogger();  logger.writeLog();  }  
}

 优缺点:

关键点:1、保证工厂方法内对修改关闭;2、如果要更换另一种产品,仍然需要修改实例化的具体工厂类;3、使得客户端和具体实现端分离,实现面向接口编程 

例子: 

抽象工厂 

定义:

选择产品簇的实现。

工厂方法是选择单个产品的实现,虽然一个类里面可以有多个工厂方法,但是这些方法之间一般是没有联系的,即使看起来像有联系。
抽象工厂着重的就是为 一个产品簇选择实现 ,定义在抽象工厂里面的方法通常是有联系的,它们都是产品的某一部分或者是相互依赖的。如果抽象工厂里面只定义一个方法,直接创建产品,那么就退化成为工厂方法了。

联系:

工厂方法模式一般是针对 单独的产品对象 的创建,而抽象工厂模式注重 产品簇对象的创建 ,这是它们的区别。
如果把抽象工厂创建的产品簇简化,这个产品簇就只有一个产品,那么这个时候的抽象工厂跟工厂方法是差不多的,也就是抽象工厂可以退化成工厂方法,而工厂方法又可以退化成简单工厂,这是它们的联系。

类图:

代码:

抽象部分(抽象工厂、抽象产品)

/** * 抽象工厂的接口,声明创建抽象产品对象的操作 */
public interface Factory {    /**     * 示例方法,创建抽象产品A的对象     @return 抽象产品A的对象     */    public ProductA createProductA();    /**     * 示例方法,创建抽象产品B的对象     @return 抽象产品B的对象     */    public ProductB createProductB();
}
/**  * 抽象产品A的接口 */  
public interface ProductA {  //定义抽象产品A相关的操作  
}
/**  * 抽象产品B的接口 */  
public interface ProductB {  //定义抽象产品B相关的操作  
} 

 具体部分(具体产品+具体工厂)

/** 
/ * 产品A的具体实现 */  
public class ProductA1 implements ProductA {  //实现产品A的接口中定义的操作  
} 
public class ProductA2 implements ProductA {  //实现产品A的接口中定义的操作  
} 
//ProductB系列是类似的代码
/**  * 具体的工厂实现对象,实现创建具体的产品对象的操作  */  
public class Factory1 implements Factory {  public ProductA   createProductA() {  return new ProductA1();  }  public ProductB    createProductB() {  return new ProductB1();  }  
}  
/**  * 具体的工厂实现对象,实现创建具体的产品对象的操作  */  
public class Factory2 implements Factory {  public ProductA    createProductA() {  return new ProductA2();  }  public ProductB    createProductB() {  return new ProductB2();  }  
}  

客户端

public class Client {  public static void main(String[] args) {  //创建抽象工厂对象  Factory af = new Factory1();  //通过抽象工厂来获取一系列的对象,如产品A和产品B  af.createProductA();  af.createProductB();  }  
}  

总结 

简单工厂:一个工厂生产所有的商品。耦合性太强,不符合开闭等原则。但是实现客户端和具体商品创建的解耦,也一定程度上实现了具体实现的封装

工厂方法:对工厂按照生产不同商品进行分离,不同工厂生产不同的商品。进一步对程序进行解耦,更易达成开闭原则等(假如总共只有一种商品,那么工厂方法就退化为简单工厂)

抽象工厂:存在一个超级大厂生产产品簇,能够生产具体的工厂,不同工厂协作生产出一个商品(本质:一个工厂能够生产多个产品)(假如产品簇只有一个产品,那么一个工厂就能生产一个产品,因此抽象工厂退化为工厂方法)

总结

如果觉得写的还不错,可以点个赞收藏一下呀~~

祝大家学业、事业、爱情顺利!

天天开心,没有Bug每一天

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/29918.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【AI学习】LLaMA 系列模型的进化(一)

一直对LLaMA 名下的各个模型关系搞不清楚&#xff0c;什么羊驼、考拉的&#xff0c;不知所以。幸好看到两篇综述&#xff0c;有个大致了解&#xff0c;以及SEBASTIAN RASCHKA对LLaMa 3的介绍。做一个记录。 一、文章《Large Language Models: A Survey》中对LLaMa的介绍 论文…

【微服务网关——负载均衡】

1. 四大负载均衡策略 随机负载 随机挑选目标服务器IP 轮询负载 ABC三台服务器&#xff0c;ABCABC依次轮询 加权负载 给目标设置访问权重&#xff0c;按照权重轮询 一致性hash负载 请求固定URL访问指定IP 2.随机负载均衡 可以通过random函数来随机选择一个ip 2.1 代码实现 …

陶建辉入选 2023 年度“中国物联网行业卓越人物榜”

在这个技术飞速发展的时代&#xff0c;物联网行业作为推动社会进步的重要力量&#xff0c;正在不断地演化和革新。近日&#xff0c;中国智联网生态大会暨“2023 物联之星”年度榜单颁奖典礼在上海浦东举行。现场公布了拥有物联网行业奥斯卡奖之称的 ——“物联之星 2023 中国物…

「51媒体」上海电视台媒体邀约专访怎么做?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 电视台专访通常会对一些热门话题&#xff0c;行业热点&#xff0c;或者新闻焦点&#xff0c;邀请嘉宾进行访谈。企业如果想要在电视台进行专访&#xff0c;通常要有合适的时机和选题。 下…

【面试干货】Java方法重写的规则

【面试干货】Java方法重写的规则 1、Java方法重写的规则2、示例代码3、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在Java中&#xff0c;方法重写&#xff08;Overriding&#xff09;是面向对象编程中的一个核心概念&#xff0c;它…

结硬寨:联想服务器的向前之路

曾国藩曾经将自己的战略思想&#xff0c;总结为“结硬寨&#xff0c;打呆仗”。 这种稳健的策略&#xff0c;往往在真实的产业发展中能收获奇效。我们喜欢听颠覆性的产业创新&#xff0c;却往往忽视稳扎稳打&#xff0c;把每一个优势聚集起来形成整体优势&#xff0c;可能才是市…

在Python项目中自定义日志工具

在Python项目中自定义日志工具 日志记录是软件开发中的一个关键部分&#xff0c;它可以帮助开发人员调试代码、监控运行状况以及记录重要事件。在Python中&#xff0c;logging 模块提供了强大的日志记录功能。本文将介绍如何创建一个日志工具&#xff0c;使其能够同时将日志输…

【ajax基础02】URL详解

目录 一&#xff1a;什么是URL 二&#xff1a;URL组成 协议 ​编辑 域名&#xff08;在url中必须写&#xff09; 资源路径 三&#xff1a;URL查询参数 定义&#xff1a; 语法格式&#xff1a; 如何利用axios实现特定数据查询&#xff1a; 语法格式: 案例&#xff1a…

【耐水好】强耐水UV胶水它的粘接强度和普通UV胶水比如何呢

【耐水好】强耐水UV胶水它的粘接强度和普通UV胶水比如何呢 强耐水UV胶水的粘接强度与普通UV胶水相比&#xff0c;具有显著的优势。以下是详细的比较和归纳&#xff1a; 固化方式&#xff1a; 两者都是通过紫外线&#xff08;UV&#xff09;照射进行固化&#xff0c;但强耐水UV…

2024年全球架构师峰会(ArchSummit深圳站)

前言 ArchSummit全球架构师峰会是极客邦科技旗下InfoQ中国团队推出的重点面向高端技术管理者、架构师的技术会议&#xff0c;54%参会者拥有8年以上工作经验。 ArchSummit聚焦业界强大的技术成果&#xff0c;秉承“实践第一、案例为主”的原则&#xff0c;展示先进技术在行业中的…

程序员学CFA——经济学(三)

经济学&#xff08;三&#xff09; 总产出、价格水平和经济增长总产出、总收入和总支出总产出、总收入和总支出的概念及联系国内生产总值国内生产总值&#xff08;GDP&#xff09;的定义GDP的衡量方法GDP的相关概念GDP的核算方法 总需求、总供给和市场均衡总需求总需求及其假设…

Netty中Reactor线程的运行逻辑

Netty中的Reactor线程主要干三件事情&#xff1a; 轮询注册在Reactor上的所有Channel感兴趣的IO就绪事件。 处理Channel上的IO就绪事件。 执行Netty中的异步任务。 正是这三个部分组成了Reactor的运行框架&#xff0c;那么我们现在来看下这个运行框架具体是怎么运转的~~ 这…

设计模式-结构型-06-桥接模式

1、传统方式解决手机操作问题 现在对不同手机类型的不同品牌实现操作编程&#xff08;比如&#xff1a;开机、关机、上网&#xff0c;打电话等&#xff09;&#xff0c;如图&#xff1a; UML 类图 问题分析 扩展性问题&#xff08;类爆炸&#xff09;&#xff1a;如果我们再…

Zygote进程的理解

Zygote进程是安卓系统的一个重要进程&#xff0c;由init进程创建而来&#xff1b;另外系统里的重要进程&#xff08;system_server等&#xff09;都是由zygote进程fork的&#xff0c;所有的app进程也是由zygote进程fork的。 一、C 里的fork函数 fork是Linux里面创建子进程的函…

电池荷电状态估计SOC?电池管理系统

一、背景 电池荷电状态&#xff08;SOC, State of Charge&#xff09;估计是电池管理系统&#xff08;BMS, Battery Management System&#xff09;的关键功能之一&#xff0c;对于确保电池的安全高效运行至关重要&#xff0c;特别是在电动车、储能系统以及便携式电子设备等领…

LCL滤波器并网逆变器双闭环控制系统仿真

并网逆变器通常采用L滤波器&#xff0c;虽然结构和控制简单&#xff0c;但是随着功率级别的增加&#xff0c;体积重量增大等问题也日益突出。为了解决这个问题&#xff0c;人们开始使用LCL滤波器&#xff0c;这种滤波器在功率较大的场合表现出色。 无源滤波器&#xff0c;又称…

神经网络模型---ResNet

一、ResNet 1.导入包 import tensorflow as tf from tensorflow.keras import layers, models, datasets, optimizersoptimizers是用于更新模型参数以最小化损失函数的算法 2.加载数据集、归一化、转为独热编码的内容一致 3.增加颜色通道 train_images train_images[...,…

Linux 系统图像化编程GTK入门

环境前期准备 演示环境&#xff1a;Windows 11 Ubuntu 22.04.4 VS Code 前提条件&#xff1a;1、Windows 11 子系统Ubuntu 22.04.4 已经安装图形化界面&#xff0c;如果没有安装请参考文章&#xff1a; windows11子系统Ubuntu 22.04.4子安装图形化界面 2、Ubuntu 22.04.4…

C语言笔记第16篇:编译和链接

1、翻译环境和运行环境 在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执行机器指令&#xff08;二进制指令&#xff09; 第2种是执行环境&#xff0c;它用于实际执行代码 2、翻译环境 那翻译环境是怎…

数据资产管理的未来趋势:洞察技术前沿,探讨数据资产管理在云计算、大数据、区块链等新技术下的发展趋势

一、引言 随着信息技术的飞速发展&#xff0c;数据已成为企业最重要的资产之一。数据资产管理作为企业核心竞争力的关键组成部分&#xff0c;其发展趋势和技术创新受到了广泛关注。特别是在云计算、大数据、区块链等新技术不断涌现的背景下&#xff0c;数据资产管理面临着前所…