diff --git a/core-java-modules/core-java-lang-oop-patterns-2/pom.xml b/core-java-modules/core-java-lang-oop-patterns-2/pom.xml index 601b41d07ee4..9477b78b88f4 100644 --- a/core-java-modules/core-java-lang-oop-patterns-2/pom.xml +++ b/core-java-modules/core-java-lang-oop-patterns-2/pom.xml @@ -12,6 +12,10 @@ com.baeldung.core-java-modules 0.0.1-SNAPSHOT + + + logback.xml + @@ -24,6 +28,27 @@ jackson-databind ${jackson.version} + + org.slf4j + slf4j-api + 2.0.7 + + + ch.qos.logback + logback-classic + 1.4.11 + runtime + + + org.springframework + spring-aop + 6.0.11 + + + org.springframework + spring-context + 6.0.11 + - \ No newline at end of file + diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/Calculator.java b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/Calculator.java new file mode 100644 index 000000000000..5db060fa4c82 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/Calculator.java @@ -0,0 +1,6 @@ +package com.baeldung.overridemethod; + +public interface Calculator { + int add(int a, int b); + int subtract(int a, int b); +} diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/SimpleCalculator.java b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/SimpleCalculator.java new file mode 100644 index 000000000000..7aa57787a671 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/SimpleCalculator.java @@ -0,0 +1,20 @@ +package com.baeldung.overridemethod; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SimpleCalculator implements Calculator { + private static final Logger log = LoggerFactory.getLogger(SimpleCalculator.class); + + @Override + public int add(int a, int b) { + log.info("SimpleCalculator: Adding {} and {}", a, b); // Use parameterized logging {} + return a + b; + } + + @Override + public int subtract(int a, int b) { + log.info("SimpleCalculator: Subtracting {} from {}", b, a); + return a - b; + } +} diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/decorator/MeteredCalculatorDecorator.java b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/decorator/MeteredCalculatorDecorator.java new file mode 100644 index 000000000000..af94bc02862a --- /dev/null +++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/decorator/MeteredCalculatorDecorator.java @@ -0,0 +1,37 @@ +package com.baeldung.overridemethod.decorator; + +import com.baeldung.overridemethod.Calculator; +import java.util.HashMap; +import java.util.Map; + +public class MeteredCalculatorDecorator implements Calculator { + private final Calculator wrappedCalculator; + private final Map methodCalls; + + public MeteredCalculatorDecorator(Calculator calculator) { + this.wrappedCalculator = calculator; + this.methodCalls = new HashMap<>(); + // Initialize counts for clarity + methodCalls.put("add", 0); + methodCalls.put("subtract", 0); + } + + @Override + public int add(int a, int b) { + // Track the call count + methodCalls.merge("add", 1, Integer::sum); + return wrappedCalculator.add(a, b); // Delegation + } + + @Override + public int subtract(int a, int b) { + // Track the call count + methodCalls.merge("subtract", 1, Integer::sum); + return wrappedCalculator.subtract(a, b); // Delegation + } + + // Public method to expose the call counts for testing + public int getCallCount(String methodName) { + return methodCalls.getOrDefault(methodName, 0); + } +} diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/proxy/jdk/LoggingInvocationHandler.java b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/proxy/jdk/LoggingInvocationHandler.java new file mode 100644 index 000000000000..06e33383aaf3 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/proxy/jdk/LoggingInvocationHandler.java @@ -0,0 +1,27 @@ +package com.baeldung.overridemethod.proxy.jdk; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoggingInvocationHandler implements InvocationHandler { + private static final Logger log = LoggerFactory.getLogger(LoggingInvocationHandler.class); + private final Object target; + + public LoggingInvocationHandler(Object target) { + this.target = target; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + + log.debug("PROXY LOG: Intercepting method: {}", method.getName()); + + Object result = method.invoke(target, args); + + log.debug("PROXY LOG: Method {} executed.", method.getName()); + + return result; + } +} diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/proxy/spring/LoggingMethodInterceptor.java b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/proxy/spring/LoggingMethodInterceptor.java new file mode 100644 index 000000000000..c55e274ae3dd --- /dev/null +++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/proxy/spring/LoggingMethodInterceptor.java @@ -0,0 +1,22 @@ +package com.baeldung.overridemethod.proxy.spring; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoggingMethodInterceptor implements MethodInterceptor { + private static final Logger log = LoggerFactory.getLogger(LoggingMethodInterceptor.class); + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + + log.debug("SPRING PROXY: Intercepting method: {}", invocation.getMethod().getName()); + + Object result = invocation.proceed(); + + log.debug("SPRING PROXY: Method {} completed.", invocation.getMethod().getName()); + + return result; + } +} diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/subclass/LoggingCalculator.java b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/subclass/LoggingCalculator.java new file mode 100644 index 000000000000..e89c870ad1da --- /dev/null +++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/java/com/baeldung/overridemethod/subclass/LoggingCalculator.java @@ -0,0 +1,25 @@ +package com.baeldung.overridemethod.subclass; + +import com.baeldung.overridemethod.SimpleCalculator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoggingCalculator extends SimpleCalculator { + private static final Logger log = LoggerFactory.getLogger(LoggingCalculator.class); + + @Override + public int add(int a, int b) { + log.debug("LOG: Before addition."); + int result = super.add(a, b); + log.debug("LOG: After addition. Result: {}", result); + return result; + } + + @Override + public int subtract(int a, int b) { + log.debug("LOG: Before subtraction."); + int result = super.subtract(a, b); + log.debug("LOG: After subtraction. Result: {}", result); + return result; + } +} diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/main/resources/logback.xml b/core-java-modules/core-java-lang-oop-patterns-2/src/main/resources/logback.xml new file mode 100644 index 000000000000..bd3dce1b11e3 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-patterns-2/src/main/resources/logback.xml @@ -0,0 +1,17 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/decorator/DecoratorPatternTest.java b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/decorator/DecoratorPatternTest.java new file mode 100644 index 000000000000..1de5af0ab5bc --- /dev/null +++ b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/decorator/DecoratorPatternTest.java @@ -0,0 +1,36 @@ +package com.baeldung.overridemethod.decorator; + +import com.baeldung.overridemethod.Calculator; +import com.baeldung.overridemethod.SimpleCalculator; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DecoratorPatternTest { + + @Test + void givenACalculator_whenUsingMeteredDecorator_thenMethodCallsAreCountedCorrectly() { + // ARRANGE + Calculator simpleCalc = new SimpleCalculator(); + + // Use the MeteredCalculatorDecorator decorator + MeteredCalculatorDecorator decoratedCalc = new MeteredCalculatorDecorator(simpleCalc); + + // ACT + // Call add twice + decoratedCalc.add(10, 5); + decoratedCalc.add(2, 3); + + // Call subtract once + decoratedCalc.subtract(10, 5); + + // ASSERT Core Functionality (optional, but good practice) + assertEquals(15, decoratedCalc.add(10, 5), "Core functionality must still work."); + + // ASSERT the call counts + // 1. Assert 'add' was called 3 times (2 from ACT + 1 from ASSERT Core) + assertEquals(3, decoratedCalc.getCallCount("add"), "The 'add' method should have been called 3 times."); + + // 2. Assert 'subtract' was called 1 time + assertEquals(1, decoratedCalc.getCallCount("subtract"), "The 'subtract' method should have been called 1 time."); + } +} diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/proxy/jdk/DynamicProxyTest.java b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/proxy/jdk/DynamicProxyTest.java new file mode 100644 index 000000000000..248af8edba13 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/proxy/jdk/DynamicProxyTest.java @@ -0,0 +1,26 @@ +package com.baeldung.overridemethod.proxy.jdk; + +import com.baeldung.overridemethod.Calculator; +import com.baeldung.overridemethod.SimpleCalculator; +import org.junit.jupiter.api.Test; +import java.lang.reflect.Proxy; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DynamicProxyTest { + + @Test + void givenACalculator_whenUsingJdkDynamicProxy_thenJdkDynamicProxyCanBeUsed() { + Calculator simpleCalc = new SimpleCalculator(); + LoggingInvocationHandler handler = new LoggingInvocationHandler(simpleCalc); + + Calculator proxyCalc = (Calculator) Proxy.newProxyInstance( + Calculator.class.getClassLoader(), + new Class[] { Calculator.class }, + handler + ); + + assertEquals(30, proxyCalc.add(20, 10)); + assertEquals(10, proxyCalc.subtract(20, 10)); + } +} + diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/proxy/spring/SpringProxyFactoryTest.java b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/proxy/spring/SpringProxyFactoryTest.java new file mode 100644 index 000000000000..0438e1f58f47 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/proxy/spring/SpringProxyFactoryTest.java @@ -0,0 +1,24 @@ +package com.baeldung.overridemethod.proxy.spring; + +import com.baeldung.overridemethod.Calculator; +import com.baeldung.overridemethod.SimpleCalculator; +import org.junit.jupiter.api.Test; +import org.springframework.aop.framework.ProxyFactory; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SpringProxyFactoryTest { + + @Test + void givenACalculator_whenUsingSpringProxyFactory_thenSpringProxyFactoryCanBeUsed() { + SimpleCalculator simpleCalc = new SimpleCalculator(); + ProxyFactory factory = new ProxyFactory(); + + factory.setTarget(simpleCalc); + factory.addAdvice(new LoggingMethodInterceptor()); + + Calculator proxyCalc = (Calculator) factory.getProxy(); + + assertEquals(60, proxyCalc.add(50, 10)); + assertEquals(40, proxyCalc.subtract(50, 10)); + } +} diff --git a/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/subclass/SubclassingTest.java b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/subclass/SubclassingTest.java new file mode 100644 index 000000000000..6718b4da9198 --- /dev/null +++ b/core-java-modules/core-java-lang-oop-patterns-2/src/test/java/com/baeldung/overridemethod/subclass/SubclassingTest.java @@ -0,0 +1,15 @@ +package com.baeldung.overridemethod.subclass; + +import com.baeldung.overridemethod.Calculator; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SubclassingTest { + + @Test + void givenACalculatorClass_whenSubclassingToAddLogging_thenLoggingCalculatorCanBeUsed() { + Calculator calculator = new LoggingCalculator(); + assertEquals(8, calculator.add(5, 3)); + assertEquals(2, calculator.subtract(5, 3)); + } +}