Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/365.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在不修改子类的情况下添加对现有子类中方法调用的控制?_Java_Class_Methods - Fatal编程技术网

Java 如何在不修改子类的情况下添加对现有子类中方法调用的控制?

Java 如何在不修改子类的情况下添加对现有子类中方法调用的控制?,java,class,methods,Java,Class,Methods,我有一些基类和一些方法void doSomething() 有不同的方法来foSomething,它们由子类1、子类2和子类3实现 现在,我想在基类中添加一个Boolean active属性,以便在实例上调用doSomething时,它只返回而不做任何操作 我知道我可以将基类编码为doSomething(),它看起来像: Void doSomething(){ if (this.getActive()) actuallyDoSomething(); } 然后在子类中重写实际的doSome

我有一些
基类
和一些方法
void doSomething()

有不同的方法来
foSomething
,它们由
子类1
子类2
子类3
实现

现在,我想在
基类
中添加一个
Boolean active
属性,以便在实例上调用
doSomething
时,它只返回而不做任何操作

我知道我可以将
基类
编码为
doSomething()
,它看起来像:

Void doSomething(){
   if (this.getActive()) actuallyDoSomething();
}
然后在子类中重写实际的doSomething(),而不是重写doSomething()。 但是感觉不对。。。在某种意义上,已经同意子类应该为
doSomething()
提供一个实现,并且它们不知道
实际的doSomething()

如果(!this.getActive())返回,我还可以让每个子类添加一个
在它的
doSomething()
实现之初,但这似乎也是错误的,因为它的公共功能我更愿意保持通用

实现这一点的常见/最佳做法是什么? 可以在不更改子类的情况下完成吗

更新

Q的重点不是设计这种功能的正确方法(非常简单), 但是关于如何在不破坏任何东西的情况下将此类功能添加到现有场景中


active
在默认情况下为true,但如果有人调用
setActive(false)
,则在任何所述子类的任何实例上,它将变为不活动,并连续调用
.doSomething()
不会做任何事情…

我可能是错的,但可能您正在搜索模板方法设计模式。 请参阅:

或者只是用谷歌搜索模式,有很多很好的网站。
希望我能提供帮助。

您想使用AspectJ提供的
@建议并执行以下操作:

// Let subClass instances run normally...
cec.setActive(true);
letThemDoSomething("BEFORE", sbc1, sbc2, sbc3);

// Now change existing scenario...
cec.setActive(false);
letThemDoSomething("AFTER", sbc1, sbc2, sbc3);
这将输出:

BEFORE ======
SubClass1: doSomething() called.
SubClass2: doSomething() called.
SubClass3: doSomething() called.

AFTER ======
Blocking instance<1> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Blocking instance<2> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Blocking instance<3> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
子类java

public class BaseClass {
    public void doSomething() {

    }

    public void say(String msg) {
         System.out.println(msg);
    }
}    
public class SubClassN extends BaseClass {
    private Integer index;

    public SubClassN(Integer index) {
        this.index = index;
    }

    @Override
    public void doSomething() {
        say("SubClass" + index + ": doSomething() called.");
    }

    public Integer getIndex() {
        return index;
    }
}
@Aspect // Mark ChangeExistingCode as the class for modifying the code 
@Component
public class ChangeExistingCode {
    private boolean active;

    public void setActive(boolean active) {
        this.active = active;
    }

    /**
     *
     * This method will be called by AspectJ anytime a `doSomething` method is called.
     *
     * This will give us a chance to decide whether the `doSomething` method should
     * be called or not.
     *
     */
    @Around("execution(* my.first.spring.aop.aspectj.BaseClass.doSomething(..))")
    public void changeExistingScenario(ProceedingJoinPoint joinPoint) throws Throwable {
        // Is active ?
        if (active) { // Yes, let doSomething() run as usual
            joinPoint.proceed();
        } else {// No, block doSomething() invokation
            Signature s = joinPoint.getSignature();

            System.out.format( //
                    "Blocking instance<%d> method: %s#%s(%s) !!\n", //
                    ((SubClassN)joinPoint.getTarget()).getIndex(), //
                    s.getDeclaringTypeName(), //
                    s.getName(), //
                    Arrays.toString(joinPoint.getArgs()) //
                    );
        }
    }
}
@Configuration // Mark the Main class as the class where Spring will find its configuration
@ComponentScan // Ask Spring to look for other components within the Main class package
@EnableAspectJAutoProxy // Let Spring auto configure AspectJ aspects for us...
public class Main {

    private static int subClassCounter;

    public static void main(String[] args) {
        subClassCounter=0;

        GenericApplicationContext  context = new AnnotationConfigApplicationContext(Main.class);

        SubClassN sbc1 = context.getBean(SubClassN.class);
        SubClassN sbc2 = context.getBean(SubClassN.class);
        SubClassN sbc3 = context.getBean(SubClassN.class);

        ChangeExistingCode cec = context.getBean(ChangeExistingCode.class);

        // Let subClass instances run normally...
        cec.setActive(true);
        letThemDoSomething("BEFORE", sbc1, sbc2, sbc3);

        // Now change existing scenario...
        cec.setActive(false);
        letThemDoSomething("AFTER", sbc1, sbc2, sbc3);

        context.close();
    }

    private static void letThemDoSomething(String prefix, SubClassN... existingClasses) {
        System.out.format("%s ======\n", prefix);
        for (SubClassN subClassInstance : existingClasses) {
            subClassInstance.doSomething();
        }
        System.out.println();
    }

    @Bean // Tell Spring to use this method for creating SubClassN instances
    @Scope(BeanDefinition.SCOPE_PROTOTYPE) // Scope prototype force creation of multiple instances
    private static SubClassN buildSubClassN() {
        subClassCounter++;
        return new SubClassN(subClassCounter);
    }
}
3-更改现有代码(不破坏任何内容…) 下面是AspectJ及其@Around建议。当调用任何
doSomething
方法时,我们将首先要求AsjectJ调用特定的方法
doSomething
可以位于
基类
或其任何子类中的任何位置

此特定方法称为
changeExistingScenario
。它可以有任何名称。这里最重要的是放在上面的注释

关于@Around值的一句话:

执行(*my.first.spring.aop.aspectj.BaseClass.doSomething(..)

此表达式仅指示我们要拦截的方法签名模式。
它将拦截基类或子类中的任何doSomething方法 有多少个参数,返回类型和访问修饰符

有关更多详细信息,请参阅:

ChangeExistingCode.java

public class BaseClass {
    public void doSomething() {

    }

    public void say(String msg) {
         System.out.println(msg);
    }
}    
public class SubClassN extends BaseClass {
    private Integer index;

    public SubClassN(Integer index) {
        this.index = index;
    }

    @Override
    public void doSomething() {
        say("SubClass" + index + ": doSomething() called.");
    }

    public Integer getIndex() {
        return index;
    }
}
@Aspect // Mark ChangeExistingCode as the class for modifying the code 
@Component
public class ChangeExistingCode {
    private boolean active;

    public void setActive(boolean active) {
        this.active = active;
    }

    /**
     *
     * This method will be called by AspectJ anytime a `doSomething` method is called.
     *
     * This will give us a chance to decide whether the `doSomething` method should
     * be called or not.
     *
     */
    @Around("execution(* my.first.spring.aop.aspectj.BaseClass.doSomething(..))")
    public void changeExistingScenario(ProceedingJoinPoint joinPoint) throws Throwable {
        // Is active ?
        if (active) { // Yes, let doSomething() run as usual
            joinPoint.proceed();
        } else {// No, block doSomething() invokation
            Signature s = joinPoint.getSignature();

            System.out.format( //
                    "Blocking instance<%d> method: %s#%s(%s) !!\n", //
                    ((SubClassN)joinPoint.getTarget()).getIndex(), //
                    s.getDeclaringTypeName(), //
                    s.getName(), //
                    Arrays.toString(joinPoint.getArgs()) //
                    );
        }
    }
}
@Configuration // Mark the Main class as the class where Spring will find its configuration
@ComponentScan // Ask Spring to look for other components within the Main class package
@EnableAspectJAutoProxy // Let Spring auto configure AspectJ aspects for us...
public class Main {

    private static int subClassCounter;

    public static void main(String[] args) {
        subClassCounter=0;

        GenericApplicationContext  context = new AnnotationConfigApplicationContext(Main.class);

        SubClassN sbc1 = context.getBean(SubClassN.class);
        SubClassN sbc2 = context.getBean(SubClassN.class);
        SubClassN sbc3 = context.getBean(SubClassN.class);

        ChangeExistingCode cec = context.getBean(ChangeExistingCode.class);

        // Let subClass instances run normally...
        cec.setActive(true);
        letThemDoSomething("BEFORE", sbc1, sbc2, sbc3);

        // Now change existing scenario...
        cec.setActive(false);
        letThemDoSomething("AFTER", sbc1, sbc2, sbc3);

        context.close();
    }

    private static void letThemDoSomething(String prefix, SubClassN... existingClasses) {
        System.out.format("%s ======\n", prefix);
        for (SubClassN subClassInstance : existingClasses) {
            subClassInstance.doSomething();
        }
        System.out.println();
    }

    @Bean // Tell Spring to use this method for creating SubClassN instances
    @Scope(BeanDefinition.SCOPE_PROTOTYPE) // Scope prototype force creation of multiple instances
    private static SubClassN buildSubClassN() {
        subClassCounter++;
        return new SubClassN(subClassCounter);
    }
}
输出

BEFORE ======
SubClass1: doSomething() called.
SubClass2: doSomething() called.
SubClass3: doSomething() called.

AFTER ======
Blocking instance<1> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Blocking instance<2> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
Blocking instance<3> method: my.first.spring.aop.aspectj.SubClassN#doSomething([]) !!
之前======
子类1:doSomething()被调用。
子类2:doSomething()被调用。
子类3:doSomething()被调用。
之后======
阻塞实例方法:my.first.spring.aop.aspectj.subclass#doSomething([])!!
阻塞实例方法:my.first.spring.aop.aspectj.subclass#doSomething([])!!
阻塞实例方法:my.first.spring.aop.aspectj.subclass#doSomething([])!!
5-参考文献
  • 下载完整代码:

  • 有助于撰写此答案的其他有用资源


    • 因此,我想我应该做以下几点:

      doSomething

      public final void doSomthing(boolean testBeforeDoing, ..../*original params*/) {
         if (!testBeforeDoing || this.getIsActive()) {
               doSomething(..../*original params*/)
         }
      }
      

      当然,还要在基类中添加对
      isActive
      的支持

      因此,现在任何用于调用基类或任何具有旧签名的子类上的
      doSomething
      的旧代码仍将运行相同的(即忽略实例的活动/非活动状态)

      任何想要使用新功能停用实例的代码都可以调用
      .setIsActive(bool)
      ,并将
      true
      作为他对
      doSomething
      所做任何调用的第一个参数

      通过这种方式,我可以获得以下优势: 1) 向后兼容性 2) 子类完全不需要更改 3) 在希望使用新功能时进行最小更改

      但这种方式有一些局限性, 例如,如果
      doSomething
      已经有两个签名在开始时相差一个布尔值,则这不起作用

      i、 e.如果我有
      doSomething(布尔b,int i)
      doSomething(布尔a,布尔b,int i)
      在第一个签名中添加
      bool testbeforedo
      将导致
      doSomething(boolean testbeforedo,boolean b,int i)
      ,这与
      doSomething(boolean a,boolean b,int i)
      无法区分,因此无法完成

      因此,我最终做了以下几点:

      我将只向基类添加一个
      dosomthingingifitcanbedone
      ,而不是在执行之前添加
      boolean测试。。并让它调用
      doSomething()

      所以

      当然,还要在基类中添加对
      isActive
      的支持

      注意,这种方式
      someThingCanBeDone()
      也可以被子类覆盖,以提供关于何时可以调用doSomething和何时不能调用doSomething的额外限制

      @Override
      public boolean someThingCanBeDone() {
        return base.someThingCanBeDone() && someConditionLocalToTheSubclass;
      }
      

      使
      实际成为方法()
      摘要
      pro