Repository: TypeScript
Issue: Proposal: delegateof
Labels: Suggestion,In Discussion

# delegateof Proposal

**delegate (noun)** 

A person sent or authorized to represent others, in particular an elected representative sent to a conference.
___

## Motivation

A popular and widely applied practice for creating flexible and reusable object oriented code is **delegation**.

**Delegation** involves two objects handling a request: a receiving object(**delegator**) delegates operations to its **delegate**.

An example of this is the [Strategy Pattern](https://en.wikipedia.org/wiki/Strategy_pattern).


## Problem

In order for the delegate pattern to be successful, **the delegate must be able to have access to the same context as the delegator**.

While the **delegator** context can be passed to the **delegate** in the form of raw parameters (`delegateOperation(delegateRequiredParam1,delegateRequiredParam2,delegateRequiredParam3)`) this approach restricts the amount of responsibility that can be delegated in a nice, scalable way.

To remove such restriction, the **delegate** should receive a reference to the **delegator** object and have the possibility of accessing a wider interface than the one of a foreign caller. 

**Example**

### Given

```typescript
class Player {
    protected xPos: number;
    protected yPos: number;

    constructor(private movementStrategy: PlayerMovementStrategy) {
    }

    protected startAnimation(animationId: string) {
        // Code to start an animation
    }
    
    protected setMovementStrategy(movementStrategy: PlayerMovementStrategy){
        this.movementStrategy = movementStrategy;
    }

    move(direction: Direction) {
        this.movementStrategy.move(this, direction);
    }
}

class WalkingMovementStrategy implements PlayerMovementStrategy {
    move(player: Player, direction: Direction) {
        player.startAnimation('someWalkingAnimation');
        switch (direction) {
            case Direction.UP:
                player.setMovementStrategy(flyingStrategy);
                break;
            case Direction.Right:
                player.xPos = player.xPos + 1;
            // Rest of cases    
        }
    }
}

class FlyingMovementStrategy implements PlayerMovementStrategy {
    move(player: Player, direction: Direction) {
        player.startAnimation('flyingAnimation');
        switch (direction) {
            case Direction.Up:
                player.yPos = player.yPos + 10;
                break;
            // Rest of cases    
        }
    }
}

interface PlayerMovementStrategy {
    move(player: Player, direction: Direction): void;
}

enum Direction {
    Up,
    Down,
    Left,
    Right,
}
```


### Then

```typescript
const coolPlayer = new Player(new WalkingMovementStrategy());
coolPlayer.move(Direction.Up);
```

```sh
 Property startAnimation is protected an is only accessible within the class Player.
```

Things to note

1. 
Changing the `startAnimation` member accessibility to public solves the problem but exposes a method intended for internal usage.
Same goes for the other attributes xPos, yPos.

2. 
The delegate must change more than 1 thing of the delegator object, hence making returning a value to apply the changes possible but ugly. 

Something like this
```typescript
const result = movementStrategy.move(player, direction);
console.log(result);
// { nextAnimation: 'walkingAnimation', newYPos : 2 }
```

And what about extensibility? What happens if a movement strategy wants to trigger multiple animations?


## Solution

Add the functionality: `objectA delegateof objectB`

### What does it do?

`delegateof` allows `objectA` to have access to the protected members of `objectB`.

### Example

```typescript
class Player{
    constructor(private movementStrategy: PlayerMovementStrategy){}
    protected startAnimation(animationId: string){
        // start the animation
    }
    move(direction: Direction){
        this.movementStrategy.move(this, direction);
    }
}

class WalkingMovementStrategy delegateof Player implements PlayerMovementStrategy{
    move(player: Player, direction: Direction){
        // No member accesibility errors yay
        player.startAnimation('yayAnimation');
    }
}
```

### Closing thoughts

While the keyword suffices for the OOP style, I am not sure how could this be implemented for plain objects or if it should even be attempted to.

Example
```typescript
const playerMovementStrategy = {
    move(player: Player, direction: Direction){
        // How to make this work here... or do we even need this?
        player.startAnimation('yayAnimation');
    }
}
```

Eager to hear your thoughts on it,

Joel.

### Edit  on member privacy
@andy-ms mentioned privacy as a concern. If we would like to retain the privacy of protected and private members we could introduce a new accessor `delegated` which functions as `protected` but allows classes which declare themselves as delegates of the class to be able to access the member.

```typescript
class Player{
    constructor(private movementStrategy: PlayerMovementStrategy){}

    delegated startAnimation(animationId: string){
        // start the animation
    }

    move(direction: Direction){
        this.movementStrategy.move(this, direction);
    }

}

class WalkingMovementStrategy delegateof Player implements PlayerMovementStrategy{

    move(player: Player, direction: Direction){
        // No member accesibility errors ;-)
        player.startAnimation('yayAnimation');
    }

}
```