基于接口编程
程序 以 类 为 单位, 组织开发。
类,本身就是一种抽象,抽象了 真实存在 的 对象 。这就像 蛋糕模具,模具总是完整的,但做出来的蛋糕可能是有残角的。我们定义类,就是定义了一个完整的模具,实例化的对象可能属性值为空,或是 方法从未被使用过。
类 的 特性:
1. 封装-访问控制:每个事物有自己的边界、内在关联
2. 抽象 -接口、抽象类、函数、宽泛命名、添加注释:抽象简化问题,只关注功能不关心实现细节
3. 继承+多态-重写、实现,超出四层用 -> 组合,扁平化
接口 定义了 功能。飞翔 接口。其 实现类 大雁会飞,飞蛾会飞。当一个接口有不止一个实现类时,接口代替类,可以 快速做类的变更。
WildGoose wildGoose =new WildGoose();
// 大雁飞
wildGoose.fly();
->
Moth moth =new Moth();
// 飞蛾飞
moth.fly();
Fly fly =new WildGoose();
// 大雁飞
fly.fly();
->
Fly fly =new Moth();
// 飞蛾飞
fly.fly();
接口与抽象类
接口 表达 功能。自上而下 设计。
抽象类 表达 有继承关系,但需要 抽象方法。自下而上 多态。
抽象类定义一致的控制逻辑
一定要定义接口吗
MVC 框架 Service 接口还是很有用的,刚创建可能用处不大,不要给以后埋坑。
service层的接口有什么用? - 知乎 (zhihu.com)
接口多个实现类时的问题
1. 把类的创建耦合在你的业务逻辑中就不好维护了,得抽离出来。
2. 或是你该做的灵活一点,根据业务请求 决定 使用 哪个实现类。
3. 每次需要类时就创建一个类,类创建和销毁操作 导致 资源浪费明显。
模式
单例模式
1. 饿汉式
- private static final
- public static
不常用
2. 懒汉式
```java
//懒汉式-- 实例方法 调用时,类进行实例化。 synchronized,不支持高并发
private static IdGenerator lazyInstance;
public static synchronized IdGenerator getLazyInstance() {
if (lazyInstance == null) {
lazyInstance = new IdGenerator();
}
return lazyInstance;
}
// 调用
IdGenerator.getLazyInstance();
```
3. 双重检测
```java
//双重检测,实例化后不会再进入加锁逻辑
//高版本的JDK 把对象 new 操作和初始化操作设计为原子操作,能禁止指令重排
// volatile 关键字则保证了指令的顺序性(禁止指令重排)和对象初始化的正确性(指令重排引起)。
private volatile static IdGenerator doubleCheckInstance; //DCL(双重检测锁)
public static IdGenerator getDoubleCheckInstance() {
if (doubleCheckInstance == null) {
// 加锁保证变量的可见性和线程安全性
// 加类锁
synchronized (IdGenerator.class) {
if (doubleCheckInstance == null) {
doubleCheckInstance = new IdGenerator();
}
}
}
return doubleCheckInstance;
}
// 调用
IdGenerator.getDoubleCheckInstance();
```
4. 静态内部类
- insance 的唯一性、创建过程的线程安全性,都由 JVM 来保证
- private static class
- private static final
```java
// IdGenerator 类内
/**
* 静态内部类,insance 的唯一性、创建过程的线程安全性,都由 JVM 来保证
* @return
*/
public static IdGenerator getStaticInstance() {
return SingletonHolder.instance;
}
/**
* 内部类的方式,实现单例懒加载
* 因为需要方法到 内部类的 static 变量,所以才声明内部类为 static 类
* 为了避免滥用内部类,内部类 用 private 修饰
*/
private static class SingletonHolder {
/**
* 类的 static变量(必须是 非基本/字符串类型 或者 非final修饰)在初始化时赋值,JVM确保初始化时的线程安全性
*
* 避免this引用逃逸:
* 1. 在构造函数中避免启动新线程或将"this"引用传递给其他对象。
* 2. 避免在构造函数中调用外部类或静态方法,并将"this"引用传递给它们。
* 3. 将构造函数声明为私有,并使用工厂方法来创建对象,确保对象完全构造完成后再对外暴露。
* 如果能避免this引用逃逸,那final修饰的变量可以确保只被赋值一次
*/
public static final IdGenerator instance = new IdGenerator();
}
// 调用
IdGenerator.getStaticInstance();
```
5. 枚举
- public enum
- INSTANCE;
```java
/**
* 单例,枚举实现
*/
public enum IdGeneratorEnum {
INSTANCE;
}
// 调用
IdGeneratorEnum.INSTANCE;
```
工厂模式
1. 静态工厂
A-A.1
静态工厂在于 两级 A-A.1
2. 工厂方法
A.1-B.1/B.2
工厂方法在于 B可选为 B.1/B.2
组合通过Map预存所有来实现
```java
// ElfBlacksmith 初始化
static {
// EnumMap,一个map, key 为 枚举类型
ELFARSENAL = new EnumMap<>(WeaponType.class);
Arrays.stream(WeaponType.values()).forEach(type -> ELFARSENAL.put(type, new ElfWeapon(type)));
}
// 使用 ElfBlacksmith#manufactureWeapon
ELFARSENAL.get(weaponType);
```
3. 抽象工厂
A-A.1-B.1 ; A-A.2-B.2
抽象工厂在于 三级A-A.1-B.1
一级静态工厂,KingdomType 实现
```java
// 声明
@RequiredArgsConstructor
@Getter
public enum KingdomType {
ELF(ElfKingdomFactory::new),
ORC(OrcKingdomFactory::new);
private final Supplier<KingdomFactory> constructor;
}
// 使用 关键字获取实例
(Kingdom.FactoryMaker.KingdomType kingdomType)kingdomType.getConstructor().get();
```
高分作业spring-beans
Java Spring 框架 spring-beans 模块对 这个问题做了好的解答。Spring这个底层框架帮你管理 类创建和类间依赖。
Spring解决 类创建和业务代码耦合问题:
1. 引入Spring框架
2. 注解 声明 类 加入Spring管理
3. 注解 引用 类对象
Spring解决 一个接口多个实现类问题:
1. 引入Spring框架
2. 在每个实现类 上 声明 类 加入Spring管理
3. 注解,用Map(实现类名首字母小写,实现类) .get(实现类名首字母小写) 获取实现类
Spring解决 类 频繁创建和销毁的资源浪费问题:
Spring创建的类默认是 单例类
看实现
基于 spring-beans-5.3.22.jar
Factory,工厂,一种创建类的设计模式,Spring 把 他管理的 类 叫做 Bean。所以有 顶层接口 BeanFactory。
// 查找 Bean 声明
// org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
/**
* Build and validate a configuration model based on the registry of
* {@link Configuration} classes.
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
}
// 创建Bean
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
/**
* The name of the currently created bean, for implicit dependency registration
* on getBean etc invocations triggered from a user-specified Supplier callback.
*/
private final NamedThreadLocal<String> currentlyCreatedBean = new NamedThreadLocal<>("Currently created bean");
/**
* Actually create the specified bean. Pre-creation processing has already happened
* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
* <p>Differentiates between default bean instantiation, use of a
* factory method, and autowiring a constructor.
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a new instance of the bean
* @throws BeanCreationException if the bean could not be created
* @see #instantiateBean // 实例化Bean
* @see #instantiateUsingFactoryMethod // 使用工厂方法 实例化 BeanWrapper
* @see #autowireConstructor // 反射创建Bean
*/
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean. 实例化Bean
BeanWrapper instanceWrapper = = createBeanInstance(beanName, mbd, args);
// Bean 依赖处理
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
/**
* Create a new instance for the specified bean, using an appropriate instantiation strategy:
* factory method, constructor autowiring, or simple instantiation.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @param args explicit arguments to use for constructor or factory method invocation
* @return a BeanWrapper for the new instance
* @see #obtainFromSupplier // 存储创建的Bean
* @see #instantiateUsingFactoryMethod
* @see #autowireConstructor
* @see #instantiateBean
*/
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
/**
* Create a new AbstractAutowireCapableBeanFactory.
*/
public AbstractAutowireCapableBeanFactory() {
// Bean 创建策略
if (NativeDetector.inNativeImage()) {
// nstantiate a class using the given constructor.
this.instantiationStrategy = new SimpleInstantiationStrategy();
}
else {
// generate CGLIB subclass.
this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();
}
}
/**
* Instantiate the given bean using its default constructor.
* @param beanName the name of the bean
* @param mbd the bean definition for the bean
* @return a BeanWrapper for the new instance
*/
protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
// 选用Bean 创建策略
// 工厂方法模式 创建Bean
Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
}
/**
* Instantiate the bean using a named factory method. The method may be static, if the
* bean definition parameter specifies a class, rather than a "factory-bean", or
* an instance variable on a factory object itself configured using Dependency Injection.
* <p>Implementation requires iterating over the static or instance methods with the
* name specified in the RootBeanDefinition (the method may be overloaded) and trying
* to match with the parameters. We don't have the types attached to constructor args,
* so trial and error is the only way to go here. The explicitArgs array may contain
* argument values passed in programmatically via the corresponding getBean method.
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
* @param explicitArgs argument values passed in programmatically via the getBean
* method, or {@code null} if none (-> use constructor argument values from bean definition)
* @return a BeanWrapper for the new instance
*/
public BeanWrapper instantiateUsingFactoryMethod(
String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
BeanWrapperImpl bw = new BeanWrapperImpl();
String factoryBeanName = mbd.getFactoryBeanName();
// 抽象工厂模式,获取工厂的工厂
Object factoryBean = this.beanFactory.getBean(factoryBeanName);
bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));
return bw;
}
/**
* Fill in any missing property values with references to
* other beans in this factory if autowire is set to "byName".
* @param beanName the name of the bean we're wiring up.
* Useful for debugging messages; not used functionally.
* @param mbd bean definition to update through autowiring
* @param bw the BeanWrapper from which we can obtain information about the bean
* @param pvs the PropertyValues to register wired objects with
*/
protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
if (containsBean(propertyName)) {
// 创建依赖
Object bean = getBean(propertyName);
// 保存成 Bean的属性值
pvs.add(propertyName, bean);
// 把 依赖关系存入Map
registerDependentBean(propertyName, beanName);
}
}
// Bean间依赖
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerDependentBean
/** Map between dependent bean names: bean name to Set of dependent bean names. */
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
/** Map between depending bean names: bean name to Set of bean names for the bean's dependencies. */
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
/** Cache of singleton objects: bean name to bean instance. 已经创建的单例*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Names of beans that are currently in creation. 正在创建的单例*/
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/**
* Register a dependent bean for the given bean,
* to be destroyed before the given bean is destroyed.
* @param beanName the name of the bean
* @param dependentBeanName the name of the dependent bean
*/
public void registerDependentBean(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName);
synchronized (this.dependentBeanMap) {
Set<String> dependentBeans =
this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
if (!dependentBeans.add(dependentBeanName)) {
return;
}
}
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean =
this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
dependenciesForBean.add(canonicalName);
}
}
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular referenc
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or
* @return the registered singleton object, or {@code null} if none found
单例模式:双重检查锁 获取 单例
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference)
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
}
}
}
创建类,创建类只有两种方式:
1. new
2. 反射
显然,Spring 通过反射创建类。
管理类间依赖:BeanA 依赖 BeanB
1. 创建 BeanB
2. BeanB 加入到 BeanA 属性值
3. 建立 Map<BeanA,BeanB>
Spring 使用Map 管理 Bean 和Bean间依赖。
类创建使用的设计模式:
1. 抽象工厂模式,输入 Bean工厂 名称,输出 工厂实例
2. 工厂方法模式,输入 Bean 名称,输出 Bean实例
3. 单例模式:双重检查锁,获取全局唯一实例
接口划分粒度
接口要定义的够 抽象、通用。
原则上,接口一旦定义好,就不能再做改动。-> 不改老代码
那如何基于老代码做扩展呢?下篇,类扩展。