With the Callable interface that was introduced in Winter '19 you can now build a light weight interface for the methods you want to dynamically call from a class.
The example below is from the docs (tweaked to show dynamic method naming):
Example class you want to dynamically call
public class Extension implements Callable {
// Actual method
String concatStrings(String stringValue) {
return stringValue + stringValue;
}
// Actual method
Decimal multiplyNumbers(Decimal decimalValue) {
return decimalValue * decimalValue;
}
// Dispatch actual methods
public Object call(String action, Map<String, Object> args) {
switch on action {
when 'concatStrings' {
return this.concatStrings((String)args.get('stringValue'));
}
when 'multiplyNumbers' {
return this.multiplyNumbers((Decimal)args.get('decimalValue'));
}
when else {
throw new ExtensionMalformedCallException('Method not implemented');
}
}
}
public class ExtensionMalformedCallException extends Exception {}
}
Unit test demonstrating the dynamic calling
@IsTest
private with sharing class ExtensionCaller {
@IsTest
private static void givenConfiguredExtensionWhenCalledThenValidResult() {
// Given
String className = 'Extension'; // Variable to demonstrate setting class name
String methodName = 'multiplyNumbers'; // Variable to demonstrate setting method name
Decimal decimalTestValue = 10;
// When
Callable extension = (Callable) Type.forName(className).newInstance();
Decimal result = (Decimal) extension.call(methodName, new Map<String, Object> { 'decimalValue' => decimalTestValue });
// Then
System.assertEquals(100, result);
}
}