Search code examples
typescriptalgorithmcoding-styledry

Clean way to respect DRY for conditions


I've got this piece of code in typescript (although the language doesn't really matter):

let name = '', parentId = '';
if (obj instanceof Service) {
    name = obj.name;
} else if (obj instanceof Method) {
    name = obj.name;
    parentId = this.generateUUID(obj._parentService);
} else if (obj instanceof Argument) {
    name = obj.name;
    parentId = this.generateUUID(obj._parentMethod);
}

I could do the same thing with a case statement, but it wouldn't change the problem: I have a repetition of name = obj.name; 3 times

So I could change the code to something like:

let name = '', parentId = '';

if(obj instanceof Service || obj instanceof Method || obj instanceof Argument)
    name = obj.name;

if (obj instanceof Method) {
    parentId = this.generateUUID(obj._parentService);
} else if (obj instanceof Argument) {
    parentId = this.generateUUID(obj._parentMethod);
}

But then I have a condition repetition that I don't really like..

Is there a way to achieve this without repetition, and with good readability?


Here is a minimal reproductible example of my problem:

class A {name:string=''}
class B {name:string=''; parentA: A = new A()}
class C {name:string=''; parentB: B = new B()}

function hash(s: string): string{
  return '' + s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);              
}

function generateUUID(obj: A | B | C) {
    let name = '', parentId = '';

    if (obj instanceof A) {
        name = obj.name;
    } else if (obj instanceof B) {
        name = obj.name;
        parentId = generateUUID(obj.parentA);
    } else if (obj instanceof C) {
        name = obj.name;
        parentId = generateUUID(obj.parentB);
    }

    return hash(parentId+name);
}

const a = new A();
a.name = 'a';

const b = new B();
b.name = 'b';
b.parentA = a;

const c = new C();
c.name = 'c';
c.parentB = b;


console.log(
    generateUUID(c)
);

Solution

  • You will not be able to avoid all duplication all the time. DRY, as a principle is all about getting rid of unnecessary duplications, but does not restrict you to a situation where you cannot repeat yourself when necessary. So, keeping self-repetition to a reasonable minimum should be a compromise. I would suggest the following:

    if(obj instanceof Service || obj instanceof Method || obj instanceof Argument) {
        name = obj.name;
        if (obj.instanceof Method) {
            parentId = this.generateUUID(obj._parentService);
        } else if (obj instanceof Argument) {
            parentId = this.generateUUID(obj._parentMethod);
        }
    }
    

    You can enhance this further by implementing a base class/interface, ensuring that obj is such an instance and implementing generateUUID for it as well as for all the classes which extend it, so you will not need the inner conditionals at usage-level.