面向对象程序设计(Object Oriented Programming)
单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
单例模式就是一个应用程序中,某个类的实例对象只存在一个。构造器是被private修饰的,所以你不能用new去创建一个新的对象,我们一般通过getInstance()的方法来获取它们的实例。getInstance()的返回值是一个对象的引用,并不是一个新的实例,所以不要错误的理解成多个对象。
(1)懒汉模式:(线程不安全)
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
(2)懒汉模式:(线程安全)
1)synchronized修饰
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2) 双重校验锁(双重判断):提高1)中的效率
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
(3)饿汉模式:
1)在类加载时就提前创建好实例对象。
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
注意:
构造方法是private,是为了防止外界对该类进行创建,单例模式只允许本类中的方法调用构造函数;饿汉模式存在缺点,如果创建时,还要创建多个对象,那么可能造成空间浪费。
2)静态内部类:
饿汉模式的改进版本
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
静态内部类的优点是:外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。
博客参考:理解静态内部类单例模式
为什么单例模式的构造方法要定义为私有?
在实际开发过程中,由于调用了一个单例模式,如果单例的构造方法是public的。在调用的时候,采用了Singleton singleton = new Singleton();
调用,这样就会导致Singleton类不断的初始化,内存很快会耗尽,应用速度变慢。所以,单例模式的构造方法最好定义private并且通过Singleton.getInstance()
调用。
(4)枚举实现单例模式:
枚举类中的成员INSTANCE就相当于一个枚举类的实例,我们通过这个枚举类的构造方法传入我们想要创建的实例,因为INSTANCE这个实例是final的,这也就保证了我们创建出的实例是唯一的。通过该实例调用内部的方法,就可以实现单例了。
枚举类介绍可以参考博客。
public class User {
//私有化构造函数
private User(){ }
//定义一个静态枚举类
static enum SingletonEnum{
//创建一个枚举对象,该对象天生为单例
INSTANCE;
private User user;
//私有化枚举的构造函数
private SingletonEnum(){
user=new User();
}
public User getInstnce(){
return user;
}
}
//对外暴露一个获取User对象的静态方法
public static User getInstance(){
return SingletonEnum.INSTANCE.getInstnce();
}
}
枚举类实现单例的方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
单例模式的使用场景:
具体应用举例:
(1)简单工厂模式(静态工厂模式):
一个抽象的接口,多个抽象接口的实现类,一个工厂类,用来实例化抽象的接口;简单来说,假如现在有多个实现类C,D,E类,我们定义了一个A类(工厂类),我们可以通过向工厂类中传入适当的参数,返回需要的实现类(C,D,E)引用。
// 抽象产品类
interface Car {
public void run();
public void stop();
}
// 具体实现类
class Benz implements Car {
public void run() {
System.out.println("Benz开始启动了。。。。。");
}
public void stop() {
System.out.println("Benz停车了。。。。。");
}
}
class Ford implements Car {
public void run() {
System.out.println("Ford开始启动了。。。");
}
public void stop() {
System.out.println("Ford停车了。。。。");
}
}
// 工厂类
class Factory {
public static Car getCarInstance(String type) {
Car c = null;
if ("Benz".equals(type)) {
c = new Benz();
}
if ("Ford".equals(type)) {
c = new Ford();
}
return c;
}
}
public class Test {
public static void main(String[] args) {
Car c = Factory.getCarInstance("Benz");
if (c != null) {
c.run();
c.stop();
} else {
System.out.println("造不了这种汽车。。。");
}
}
}
对于简单的创建来说,简单工厂模式较为灵活方便,但耦合性较高,每添加一个新产品,都要重构代码。
(2)工厂方法模式:
有四个角色,抽象工厂模式,具体工厂模式,抽象产品模式,具体产品模式。不再是由一个工厂类去实例化具体的产品,而是由抽象工厂的子类去实例化产品。简单来说,创建了一个抽象的工厂类,然后用其子类去获取具体的实例引用(这个子类只能获取单个实例)。
// 抽象产品角色
public interface Moveable {
void run();
}
// 具体产品角色
public class Plane implements Moveable {
@Override
public void run() {
System.out.println("plane....");
}
}
public class Broom implements Moveable {
@Override
public void run() {
System.out.println("broom.....");
}
}
// 抽象工厂
public abstract class VehicleFactory {
abstract Moveable create();
}
// 具体工厂
public class PlaneFactory extends VehicleFactory {
public Moveable create() {
return new Plane();
}
}
// 具体工厂
public class BroomFactory extends VehicleFactory {
public Moveable create() {
return new Broom();
}
}
// 测试类
public class Test {
public static void main(String[] args) {
VehicleFactory factory = new BroomFactory();
Moveable m = factory.create();
m.run();
}
}
注意:
(3)抽象工厂模式:
围绕一个超级工厂,创建其他工厂。它有多个抽象产品类,每个抽象产品类可以派生出多个具体产品类,一个抽象工厂类,可以派生出多个具体工厂类,每个具体工厂类可以创建多个具体产品类的实例。
抽象手机
public interface Phone {
void start();
void shutdown();
}
抽象路由器
public interface Router {
void openWifi();
void shutDownWifi();
}
小米手机
public class XiaomiPhone implements Phone {
@Override
public void start() {
System.out.println("小米开机");
}
@Override
public void shutdown() {
System.out.println("小米关机");
}
}
华为手机
public class HuaweiPhone implements Phone {
@Override
public void start() {
System.out.println("华为开机");
}
@Override
public void shutdown() {
System.out.println("华为关机");
}
}
小米路由器
public class XiaomiRouter implements Router {
@Override
public void openWifi() {
System.out.println("小米wifi开启");
}
@Override
public void shutDownWifi() {
System.out.println("小米wifi关闭");
}
}
华为路由器
public class HuaweiRouter implements Router {
@Override
public void openWifi() {
System.out.println("华为wifi开启");
}
@Override
public void shutDownWifi() {
System.out.println("华为wifi关闭");
}
}
抽象工厂
public interface ProductsFactory {
Phone phoneProduct();
Router routerProduct();
}
小米工厂
public class XiaomiFactory implements ProductsFactory {
@Override
public Phone phoneProduct() {
return new XiaomiPhone();
}
@Override
public Router routerProduct() {
return new XiaomiRouter();
}
}
华为工厂
public class HuaweiFactory implements ProductsFactory {
@Override
public Phone phoneProduct() {
return new HuaweiPhone();
}
@Override
public Router routerProduct() {
return new HuaweiRouter();
}
}
测试类:
public class client {
public static void main(String[] args) {
HuaweiFactory factory = new HuaweiFactory();
Phone phone = factory.phoneProduct();
phone.start();
}
}
与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品。
产品:房子
public class Product {
private String BuildA;//地基
private String BuildB;//钢筋
private String BuildC;//粉刷
//省略:Getter/Setter/toString()
抽象产品建造者
public abstract class Builder {
abstract void buildA();
abstract void buildB();
abstract void buildC();
abstract Product getProduct();
}
具体产品建造者
public class Worker extends Builder {
Product product;
public Worker(){
product = new Product();
}
@Override
void buildA() {
product.setBuildA("铺垫地基");
}
@Override
void buildB() {
product.setBuildB("搭设钢筋");
}
@Override
void buildC() {
product.setBuildC("粉刷墙面");
}
@Override
Product getProduct() {
return product;
}
}
指挥类Director
public class Director {
public Product build(Builder builder){
builder.buildA();
builder.buildB();
builder.buildC();
return builder.getProduct();
}
}
测试类:
public class Test {
public static void main(String[] args) {
Director director = new Director();
Product build = director.build(new Worker());
System.out.println(build.toString());
}
}
指挥类Director在建造者模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是在有些情况下需要简化系统结构,可以把Derector和抽象建造者进行结合。
产品
public class Product {
private String BuildA = "汉堡";
private String BuildB = "可乐";
private String BuildC = "薯条";
private String BuildD = "甜点";
//省略:Getter/Setter/toString()
}
抽象产品建造者
public abstract class Builder {
abstract Builder buildA(String msg);
abstract Builder buildB(String msg);
abstract Builder buildC(String msg);
abstract Builder buildD(String msg);
abstract Product getProduct();
}
具体产品建造者
public class Worker extends Builder {
private Product product;
public Worker(){
product = new Product();
}
@Override
Builder buildA(String msg) {
product.setBuildA(msg);
return this;
}
@Override
Builder buildB(String msg) {
product.setBuildB(msg);
return this;
}
@Override
Builder buildC(String msg) {
product.setBuildC(msg);
return this;
}
@Override
Builder buildD(String msg) {
product.setBuildD(msg);
return this;
}
@Override
Product getProduct() {
return product;
}
}
测试:
public class Test {
public static void main(String[] args) {
Worker worker = new Worker();
Product product = worker.buildA("烤鸭").getProduct();
System.out.println(product.toString());
}
}
用clone的方式代替new创建对象的方式,效率要高一些。但是如果一个对象只有一两个字段,那么直接用new创建就好。
public class Video implements Cloneable {
private String name;
private Date createTime;
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object clone = super.clone();
Video v =(Video)clone;
v.createTime = (Date)this.createTime.clone();
return clone;
}
public Video(){}
public Video(String name,Date createTime){
this.name = name;
this.createTime = createTime;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
}
测试类:
public class Test {
public static void main(String[] args) throws CloneNotSupportedException, InterruptedException {
Video v1 = new Video("hello",new Date());
Video v2 = (Video) v1.clone();
System.out.println(v1+"*******"+v1.hashCode());
System.out.println(v2+"*******"+v2.hashCode());
Thread.sleep(2000);//为了让new Date()产生的时间不同
v2.setCreateTime(new Date());
System.out.println(v1+"*******"+v1.hashCode());
System.out.println(v2+"*******"+v2.hashCode());
}
}
结果:
Video{name='hello', createTime=Thu Oct 22 15:42:51 CST 2020}*******1735600054
Video{name='hello', createTime=Thu Oct 22 15:42:51 CST 2020}*******21685669
Video{name='hello', createTime=Thu Oct 22 15:42:51 CST 2020}*******1735600054
Video{name='hello', createTime=Thu Oct 22 15:42:53 CST 2020}*******21685669
注意:
作用:从程序的结构上实现松耦合,从而扩大整体的类结构。
分类:适配器模式、代理模式、桥接模式、装饰模式、组合模式、外观模式、享元模式
适配器模式是因为新旧接口不一致导致出现了客户端无法得到满足的问题,但是,旧的接口不能被完全丢弃掉,因为我们还想使用旧接口的一些服务。那么为了保证能够使用旧接口,且还能使用新接口,我们就应该增加一个适配器来完成新旧接口的适配。
用网口转换器来打个比喻:笔记本上没有网线接口只有USB接口,此时就需要一个适配器,将网口转换为USB口的。
类继承:类适配器
//要被适配的类:网线
public class Adaptee {
public void request(){
System.out.println("连接网线上网");
}
}
//适配器的抽象实现
public interface NetToUsb {
//连接网线上网转为连接USB上网
public void handleRequest();
}
//适配器:需要连接网线和USB
public class Adapter extends Adaptee implements NetToUsb {
@Override
public void handleRequest() {
super.request();
}
}
//电脑需要连接适配器上网
public class Computer {
public void net(NetToUsb adapter){
adapter.handleRequest();
}
public static void main(String[] args) {
Computer computer = new Computer();
Adapter adapter = new Adapter();
computer.net(adapter);
}
}
组合:对象适配器
//要被适配的类:网线
public class Adaptee {
public void request(){
System.out.println("连接网线上网");
}
}
//适配器的抽象实现
public interface NetToUsb {
//连接网线上网转为连接USB上网
public void handleRequest();
}
//对象适配器:需要连接网线和USB
public class Adapter implements NetToUsb {
private Adaptee ;
public Adapter(Adaptee adaptee){
this.adaptee = adaptee;
}
@Override
public void handleRequest() {
adaptee.request();
}
}
//电脑需要连接适配器上网
public class Computer {
public void net(NetToUsb adapter){
adapter.handleRequest();
}
public static void main(String[] args) {
Computer computer = new Computer();
Adaptee adaptee = new Adaptee();
Adapter adapter = new Adapter(adaptee);
computer.net(adapter);
}
}
注
相比于适配器的应用场景,代理就不一样了,虽然代理也同样是增加了一层,但是,代理提供的接口和原本的接口是一样的,代理模式的作用是不把实现直接暴露给client,而是通过代理这个层,代理能够做一些处理。
品牌
public interface Brand {
void info();
}
联想
public class Lenovo implements Brand {
@Override
public void info() {
System.out.println("联想");
}
}
苹果
public class Apple implements Brand {
@Override
public void info() {
System.out.println("苹果");
}
}
电脑
public abstract class Computer {
protected Brand brand;
public Computer(Brand brand){
this.brand = brand;
}
public void info(){
brand.info();
}
}
class Desktop extends Computer{
public Desktop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("台式机");
}
}
class Laptop extends Computer{
public Laptop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.println("笔记本");
}
}
测试:
public class Test {
public static void main(String[] args) {
Computer laptop = new Laptop(new Apple());
laptop.info();
Computer computer = new Desktop(new Lenovo());
computer.info();
}
}
苹果
笔记本
联想
台式机
创建了一个共有接口,该接口中可以包含若干任务。有一个代理公司类和客户类都实现了这个接口,并且代理公司类能够完成任务的大部分内容,而客户类只能完成一小部分。现在我们委托代理公司去处理任务,因为处理完任务也需要客户的参与,所以我们需要将客户类对象的引用传入代理公司类中。这时通过调用代理公司实现的方法,即可完成全部任务。
//定义一个待完成事情的接口
public interface ProcessInterface {
//需要代理的是结婚这件事,如果还有其他事情需要代理,也可以写
void marry();//代理婚庆
// void work();//代理干活
//void drive();//代理开车
}
//找到一个代理公司,来完成部分事情
public class WeddingCompany implements ProcessInterface {
private ProcessInterface processInterface;
public WeddingCompany(ProcessInterface processInterface) {
this.processInterface = processInterface;
}
@Override
public void marry() {
System.out.println("我们是婚庆公司的");
System.out.println("我们在做结婚前的准备工作");
System.out.println("节目彩排...");
System.out.println("礼物购买...");
System.out.println("工作人员分工...");
System.out.println("可以开始结婚了");
proxyInterface.marry();//需要自己完成的事情
System.out.println("结婚完毕,我们需要做后续处理,你们可以回家了,其余的事情我们公司来做");
}
}
//事情大部分被代理处理了,我们只需要完成一小部分即可。
public class NormalHome implements ProcessInterface{
@Override
public void marry() {
System.out.println("我们结婚啦~");
}
}
public class Test {
public static void main(String[] args) {
ProcessInterface processInterface = new WeddingCompany(new NormalHome());
processInterface.marry();
}
}
对已有的业务逻辑进一步的封装,使其增加额外的功能,如Java中的IO流就使用了装饰者模式,用户在使用的时候,可以任意组装,达到自己想要的效果。
class Food {
private String food_name;
public Food() {
}
public Food(String food_name) {
this.food_name = food_name;
}
public String make() {
return food_name;
};
}
class Bread extends Food {
private Food basic_food;
public Bread(Food basic_food) {
this.basic_food = basic_food;
}
public String make() {
return basic_food.make()+"+面包";
}
}
//蔬菜类
class Vegetable extends Food {
private Food basic_food;
public Vegetable(Food basic_food) {
this.basic_food = basic_food;
}
public String make() {
return basic_food.make()+"+蔬菜";
}
}
public class Main {
public static void main(String[] args) {
Food food = new Bread(new Vegetable(new Food("香肠")));
System.out.println(food.make());
}
}
香肠+蔬菜+面包
装饰器在IO流中的应用:
PrintWriter printwriter= new PrintWriter(new OutputStreamWriter(new ByteArrayOutputStream(8192), "UTF-8"));
Scanner scanner = new Scanner(new InputStreamReader(new FileInputStream("hello.txt"), "UTF-8"))
在Web应用中加入拦截器进行多层验证时,也可以使用装饰器来实现。
对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。被观察者中可以通过调用观察者对象,进而调用观察者的方法传递信息。简单来说就是A类中可以获取C类,D类等多个类的对象,并且可以执行C,D共有的方法。
观察者模式UML图:
public interface Person {
//小王和小李通过这个接口可以接收到小美发过来的消息
void getMessage(String s);
}
public class LaoWang implements Person {
private String name = "小王";
public LaoWang() {
}
@Override
public void getMessage(String s) {
System.out.println(name + "接到了小美打过来的电话,电话内容是:" + s);
}
}
public class LaoLi implements Person {
private String name = "小李";
public LaoLi() {
}
@Override
public void getMessage(String s) {
System.out.println(name + "接到了小美打过来的电话,电话内容是:->" + s);
}
}
public class XiaoMei {
List<Person> list = new ArrayList<Person>();
public XiaoMei(){
}
public void addPerson(Person person){
list.add(person);
}
//遍历list,把自己的通知发送给所有暗恋自己的人
public void notifyPerson() {
for(Person person:list){
person.getMessage("你们过来吧,谁先过来谁就能陪我一起玩儿游戏!");
}
}
}
public class Test {
public static void main(String[] args) {
XiaoMei xiao_mei = new XiaoMei();
LaoWang lao_wang = new LaoWang();
LaoLi lao_li = new LaoLi();
//小王和小李在小美那里都注册了一下
xiao_mei.addPerson(lao_wang);
xiao_mei.addPerson(lao_li);
//小美向小王和小李发送通知
xiao_mei.notifyPerson();
}
}
参考:
[1] https://blog.csdn.net/zsjlovesm521/article/details/94382666
[2] https://www.cnblogs.com/zytrue/p/8484806.html
[3] 常用的设计模式汇总,超详细!