object oriented design – Flat or nested classes

Im new to object design and Im trying to figure out which design is better.
I have a Material entity which can have multiple versions. I have two designs, one is with nested classes, and the other is flat, more like a database table.

Nested example (pseudo code):

class Material
    name
    path = "some/path/" + name
    List MaterialVersion

class MaterialVersion
    versionNumber
    path = "some/path/materialName/" + versionNumber

MaterialVersion is nested inside Material and this way its easier for me to display it in the UI.
But I’ve tought of another design like this:

class Material
    name
    version
    path = "some/path/" + name + version

class MaterialCollection
    List Material

    getUniqueMaterialsByName
    getMaterialVersions

I have lost the MaterialVersion class but I need some methods that I can query all of the materials so I can display them in a tree like this

-- Material 1
---- V1
---- V2
-- Material 2
---- V1

I think the first one is better for my use but I would like to hear some opinions or even some other solution.

object oriented design – Seeking a Pattern

First off, I totally understand that an interface cannot contain a static method because what would the method act on – that is clear. Suppose I want an interface to mandate a method that is particular to a class but acts statically (and as an interface method may be shared by many classes.)

To propose a crazy example: let’s say I have various cookers (Pressure Cooker, Wok, Microwave, Oil Fryer, Grill, Air Fryer and so on…) and I want them all to implement a Cook method to which I will pass ingredients so the method itself could be static and thus does not use any of the properties of the class. If I actually make this method static then there is no contract that I can use to constrain all these Cookers. If I create an interface with an instance method called Cook then I wonder how I could call it.

Does it make sense to create this interface? How do I call this method when I do not have an instance? Could I create a static instance of each implementing class (like Wok.Default or Wok.Empty) and use it to call the method when I only have ingredients and no instance? In this method I could decide whether I have a valid instance and either act on that instance or use passed ingredients.

Is this crazy? Is there a better way? Is there a pattern that applies to this? I’ve considered various patterns like Proxy, Facade, Adapter, and Decorator but nothing seems to exactly fit. The Mediator also seems close because I suspect it would be useful to have a ‘intercept’ in between that could identify the class thru a parameter but I am trying to not couple the method to an ‘intercept’ unless absolutely necessary. I am open to suggestion.

object oriented – Adding and Retrieving Players for a simple Pre-Flop Poker Hud with JavaScript to Local Storage, Is my design solid?

I am building a browser based Pre-Flop HUD that for now just tracks VPIP, PFR and number of hands played for an opponent (https://pokercopilot.com/poker-statistics/vpip-pfr) and using local storage to store my data. In the future I will use a better persistence strategy, add more features, and find a way to de-couple the user interface without just putting it in the functions I have now. I just wanted to use an object literal approach without classes right now and try out some basic decomposition with simple functions for practice. Any suggestions for improvement are welcome. Right now the only functionality I have is adding a player to localStorage and getting all players from local storage.

I do have a couple questions…

  1. Is there a benefit to using Classes? Right now I don’t see the benefit of having a handle, like, player1 = new Player(arg,arg,etc…), player2 = new Player(arg,arg, etc…)…

  2. If someone thinks there is a better way to store to local storage please let me know, I don’t think
    storing each player with their own key and having a ton of entries would be a good idea.

  3. In my function addPlayer() should I be calling savePlayersToStorage in my localStorageUtils.js?

Thanks!

Main.js file = entry point for my program

    /**********************************
 * This is the program driver, that
 * will call other functions
 * *******************************/

import { addPlayer } from "./player.js";
import {
  savePlayersToStorage,
  getPlayersFromStorage,
} from "./LocalStorageUtilsObjectLiteral.js";

function main() {
  let newPlayer = "Doug Polk";
  const localStorageKey = "stored_players";
  let players = getPlayersFromStorage(localStorageKey); //array of player objects in localStorage
  players.push(addPlayer(newPlayer));
  savePlayersToStorage(localStorageKey, JSON.stringify(players));
}


main();

Player.js addPlayer creates and returns an object literal

function addPlayer(name) {
  let player = {
    uuid: Date.now(),
    name: name,
    vpipActionCount: 0, //The number of times a player voluntarily put money into the pot Pre-Flop, whether calling or raising.
    pfrActionCount: 0, //The number of times a player raised pre-flop, this includes 3-bets.
    vpipPercentage: 0, //The percentage of hands a player has played, can be between 0 and 100%
    pfrPercentage: 0, //The percentage of hands a player has raised pre-flop, can be between 0 and 100%, but can never be higher than vpipPercentage
    totalHandsTracked: 0, //The number of hands tracked for a player

    callAction: function () {
      this.totalHands++;
      this.vpipActionCount++;
    },
    raiseAction: function () {
      this.totalHands++;
      this.pfrActionCount++;
    },
    foldAction: function () {
      this.totalHands++;
    },
    calculateVpipPercentage: function () {
      this.vpip = (this.vpipActionCount * 100) / this.totalHands;
    },
    calculatePfrPercentage: function () {
      this.pfrPercentage = (this.pfrActionCount * 100) / this.totalHands;
    },
    getVpipPercentage: function () {
      return this.vpipPercentage;
    },
    getPfrPercentage: function () {
      return this.pfrPercentage;
    },
  };
  return player;
}

export { addPlayer };

LocalStorageUtils.js Functions to get and save players to Local Storage

function savePlayersToStorage(localStorageKey, playersArray) {
  if (localStorage.getItem(localStorageKey === null)) {
    localStorage.setItem(localStorageKey);
  }
  localStorage.setItem(localStorageKey, playersArray);
}

function getPlayersFromStorage(localStorageKey) {
  if (localStorage.getItem(localStorageKey === null)) {
    localStorage.setItem(localStorageKey);
  }
  let players = JSON.parse(localStorage.getItem(localStorageKey));

  return players;
}

export { savePlayersToStorage, getPlayersFromStorage };

object oriented – sharing and decoupling data

I basically want to know how you would review this code and design.

Right now, there seems to be a tight coupling so I feel like I’ve violated some rules about data access and responsibility. Seems like there may be some

code smells

  • Config is shared by all tasks, allowing some tasks access to irrelevant services and data.
  • StatusObj used to save and share data throughout workflow. It also has state which should be contained in stateMachine.
  • Current task isnt defined any place. statusObj.task is only updated after success. Doesn’t seem right.
  • Starting whole worfklow inside of a block?
  • Also realizing the input of nextTask is emitted from publisher, but could also be accessed from self.config.statusObj.task. so I implement this stream to retrieve updated statusObj, but its not even necessary?

Also how this design could be extended handle interruptions, resume, cancel. What kind of general edge cases should I be thinking about.

Definitions:

  • TaskService – Initiates & manages workflow, and encapsulates required dependencies/services

  • Task – an async executable task.

  • StateMachine – manages state, provides next task in workflow
  • Publisher – Drives workflow by emitting completed tasks via StatusObj.task
  • StatusObj – Represents status of workflow. is updated by a task when data is fetched and task completes. Is also persisted in db.
  • TaskResponseHandler – Handles success, error, and completion of publisher stream. On success, it saves statusObj to db and publishes, so next task can take place.
  • Config– contains services, api’s, helpers needed for task to execute. So Config also contains the statusObj which contains value retrieved from last task.

Worflow:

I’ve defined a persist-able object statusObj that is updated throughout the workflow.

When a task completes successfully with data, it assigns that data to statusObj and emits to publisher.

responseHandler saves, and emits statusObj. responseHandler would also handle all possible errors related to task failure at this time.

last completed task via statusObj.task is used to map the next task, via stateMachine.

class TaskService {
    var sessionManager: SessionMaanger
    var config: Config
    var stateMachine: StateMachine
    var publisher: PassThroughSubject<Task, Never>
    var responseHandler: Subscriber

    @objc func execute() {
        sessionManager.requestSession(for: self)
        block = { _ in
            // can start workflow now.
            self.subscribe()
            self.publisher.send(self.config.statusObj!) // send initial statusObj.
        }
    }

    @objc func subscribe() {
        publisher
            .setFailureType(to: MyError.self)
            .map { self.nextTask(context: $0) }
            .flatMap { $0!.execute() }
            .subscribe(handler)
    }

    private func nextTask(_ code: TaskCode) -> Task? {
        // state machine has internal state thats not persisted right now but needs to.
        // has a table to output next task, given the internal state, and last completed task.
        switch (stateMachine.nextTask(task)) {
        case .A:
            return TaskA(self.config)
        case .B:
            return TaskB(self.config)
        default: return nil
        }
    }

    func resume() {

    }

    func abort() {
    }

}


class ResponseHandler: Subscriber {
    typealias Input = StatusObj?
    typealias Failure = MyError

    // from taskService
    var publisher: PassthroughSubject<StatusObj, Never>

    init(publisher: PassthroughSubject<StatusObj, Never>) {
        self.publisher = publisher
    }

    func receive(subscription: Subscription) {
        subscription.request(.max(3))
    }

    func receive(_ input: StatusObj?) -> Subscribers.Demand {
        input!.save()
        publisher.send(input!) // emit, for next task to execute
        return .unlimited
    }

    func receive(completion: Subscribers.Completion<MyError>) {
        print("stream completed")
        switch (completion) {
        case .failure(let err):
            print("error (err)")
        case .finished:
            //context.save()
            publisher.send(completion: .finished)
        }
    }
}


class TaskA {
    var config: Config
    var publisher: PassthroughSubject<StatusObj, MyError> // local publisher for async workaround

    init(_ config: Config) {
        self.config = config
    }
    func execute() -> PassthroughSubject<StatusObj, MyError> {
        completionBlock { data in
            self.config.statusObj.task = self.task
            self.config.statusObj.payloadFromTaskA = data
            publisher.send(data)
            // if error would handle here also. publisher(send: error)
        }
    }
}


class StatusObj {
    var state: State // stored in `state machine`, but if need to resume, state needs to be saved
    var task: TaskCode // enum of each task/event. 
    var payloadFromTaskA: Data
    var payloadFromTaskB: String
    var payloadFromTaskD: String
    var retryCount: Int
} 

class Config {
    var statusObj: StatusObj
    var serviceForTaskBAPI: API
    var authToken: String
}

object oriented – Resource management classes in C++ game

This is a part of simple sfml C++ game, but I think neither the library nor the language is that much crucial here. I am mainly worried about the design.

I have a class template ResourceHolder<Key, Resource> to hold one type of Resource such as texture or sound, inside a map. A key is usually an enum but can be anything.

I also have a class ResourceManager that keeps all available ResourceHolders in one place.

A simplified class diagram:

enter image description here

I am providing the full code for those classes:

ResourceHolder.h:

template <typename Key, typename Resource>
class ResourceHolder {
public:
    explicit ResourceHolder(std::string resourcesDir = "../resources/")
            : resourcesDir{std::move(resourcesDir)}
    {}

    template <typename... Args>
    void insert(const Key& key, Args&&... args) {
        auto resPtr = std::make_unique<Resource>();
        if (!resPtr->loadFromFile(resourcesDir + std::forward<Args>(args)...)) {
            msgErrorLoading(std::forward<Args>(args)...);
            ///* todo: should I e.g. "throw ErrorLoadingResource" here? */
        }
        resources.emplace(key, std::move(resPtr));
    }

    Resource& get(const Key& key) const {
        if (auto resource = resources.find(key); resource != std::end(resources)) {
            return *(resource->second);
        } throw std::invalid_argument{"No such resource id."};
    }

    void erase(const Key& key) noexcept {
        if (auto found = resources.find(key); found != std::end(resources)) {
            resources.erase(key);
        }
    }

    void eraseAll() {
        resources.clear();
    }

private:
    std::string resourcesDir;
    std::unordered_map<Key, std::unique_ptr<Resource>> resources;

public:
    template <typename... Args>
    ResourceHolder& operator+=(const ResourceInserter<Key, Args...>& inserter) {
        insert(std::move(inserter.key), std::move(std::get<Args>(inserter.args)...));
        return *this;
    }

    inline const Resource& operator()(const Key& key) const {
        return get(std::move(key));
    }

    inline Resource& operator()(const Key& key) {
        return get(std::move(key));
    }

    auto& getResources() const {
        return resources;
    }

    auto& getResourcesDir() const {
        return resourcesDir;
    }

    void setResourcesDir(std::string newPath) {
        resourcesDir = std::move(newPath);
    }

private:
    template <typename... Args>
    void msgErrorLoading(const Args... args) {
         std::cerr << "Failed loading resource: { Type: "" << typeid(Resource).name()<< "", File name: "";
        (std::cerr << ... << args) << "" }" << std::endl;
    }
};

ResourceManager.h:

class ResourceManager {
public:
    ResourceManager();

private:
    ResourceHolder<res::Texture, sf::Texture> textures;
    ResourceHolder<res::Sound, sf::SoundBuffer> sounds{"../resources/sound/"};

    void loadTextures();
    void loadSounds();

public:
    auto& getTextures() {
        return textures;
    }

    auto& getSounds() {
        return sounds;
    }
};

ResourceManager.cpp:

ResourceManager::ResourceManager() {
    loadTextures();
    loadSounds();
}

void ResourceManager::loadTextures() {
    textures.insert(res::Texture::Wizard, "wizard.png");
    textures.insert(res::Texture::Gray,   "gray.png");
    textures.insert(res::Texture::Orange, "orange.png");
}

void ResourceManager::loadSounds() {
    sounds += ResourceInserter(res::Sound::Bullet, "boing.wav");
    sounds += ResourceInserter(res::Sound::Bing,   "boing_long.wav");
    sounds += ResourceInserter(res::Sound::Poof,   "poof.wav");
}

ResourceInserter.h:

/** Operator += must take one argument
 *  This is a proxy class for operator+=
 *  You can use operator+= instead of insert() as an alternative insertion method
 */

template <typename Key, typename... Args>
class ResourceInserter {
public:
    explicit ResourceInserter(Key&& key, Args&&... args)
            : key{std::forward<Key>(key)}
            , args{std::forward<Args>(args)...}
    {}

    Key key;
    std::tuple<Args...> args;
};

template <typename T, typename... Args>
ResourceInserter(T&&, Args&&... args) -> ResourceInserter<T, Args...>;

Resources.h

namespace res {
    enum class Texture {
        Gray,
        Orange,
        Wizard
    };

    enum class Sound {
        Bullet,
        Poof,
        Bing
    };
}

Some basic usage (inside parent/caller/owner class):

auto wizardTexture = textures.get(res::Texture::Wizard);
auto bulletSound   = sounds(res::Sound::Bullet);

I am not asking for a deep-throughout review as I imagine it’d take too much off of your time.


I have few questions, answering any of them would be absolutely helpful.

  1. Whatever you think of that looks smelly or problematic, please do let me know.
  2. What’s wrong with my design from OOP/design patterns point of view? (I am especially worried about the part where I am inserting all the resources inside ResourceManager.cpp)
  3. What’s wrong with my code from C++ point of view? (I am especially interested in parts where I attempted using move semantics/perfect forwarding e.g. insert method or operator+=)
  4. Is there something confusing related to naming identifiers?

The reason why I am using enums as keys is that it works as a sort of connection interface for both insertion and then retrieval of the resource. If I insert a resource of key Enum_Type::Enum_Item, then I can also retrieve it using the same key.

I’d rather not hardcode the insertion process inside ResourceManager.cpp, and preferably keep it in a separate file, but the fact that I am using Enum as a key is kind of an obstacle here for me. Not sure how to fix it.


Thanks a lot!

Object Oriented Design of Callback Methods

Assume that there is a library and it provides an interface to its users in order to callback them. Users implement this interface and receive notifications from the library.

Let’s say, ICallback is the interface, and Notify1(arg1), …, Notify5(arg5) are the interface methods.

interface ICallback;
+ Notify1(arg1)
+ ...
+ Notify5(arg5)

This library also provides a concrete class of the ICallback and distributes this class with the library package.

class CallbackAdapter : implements ICallback
+ Notify1(arg1)
+ ...
+ Notify5(arg5)

The owner of the library calls this concrete class as “adapter”. Users are encouraged to use this class instead of interface because it is claimed that;

a. You may not want to implement all notify methods 1 to 5 because you want to keep your code clean. If you extend this adapter class instead of implementing interface directly, then you can select methods to override. (This is the main motivation written in the class document)

b. If the interface is changed, say Notify6() is added, then user don’t have to change anything in the client code. No compilation error occurs when version is bumped. (This is an extra motivation suggested by people who extends adapter class)

Note that, some overridden methods of CallbackAdapter class aren’t just empty methods, they contain code, and do some work with objects provided by library (args).

This design really disturbs me. Firstly, I will explain why I’m not comfortable with it and then I will suggest a solution to above motivations. Finally, the question will be asked at the end.

1. Favor object composition over class inheritance

When user code extends CallbackAdapter, there will be coupling between user code and an external class.
This can break the user code easily since encapsulation is terribly broken by inheriting an external class.
Anyone can have a look at Effective Java 1st Edition, Item 14 for more details.

2. Ambiguous adapter pattern

I think the adapter pattern is misused here unless this isn’t another pattern with an “adapter” postfix in the name.

As far as I know, the adapter pattern is used when there is an external alternative implementation that we want to use but our interface doesn’t match to use alternative solution directly. Hence, we write an adapter to gain capabilities of alternative implementation (adaptee).

For all the adapter examples that I’ve seen, there is an adaptation to a concrete class, a class which does a real job and have a capability. However, for the given example, the adaptation is against an interface but not a concrete class.

Is this a valid adapter as we know it? I don’t think so.

There is a statement at applicability section of adapter pattern in the book of GoF Design Patterns:

Use the Adapter pattern when you want to use an existing class, and its interface does not match the one you need.

I think developers misinterpreted the word “interface” in this statement. Author mentions of adaptee’s interface which eventually declares the methods of concrete adaptee class. It seems that developers thought like this: There will be a class that a user created, this class will implement the interface provided by us (as library developers), user will want to use this existing class now and then, one day we will change interface, and won’t match with user code, so that we must provide an adapter, and distribute this adapter with our library.

I have just tried to understand the motivation of this adapter design. Above reasoning may be wrong but this doesn’t make code safer, it’s still insecure because of 1.

3. Distribution of adapter class

If there will be an adapter class, it shouldn’t be in the library package, it should be in the user package, this makes more sense to me because user adapts his code to work with new implementations.

4. It’s not good to break contract silently

Interfaces define behavior and are used to make contracts among participants. If a contract ends and a new contract starts, I think both sides must be aware of this change. Breaking a contract silently as given in the above example, may produce undefined behavior that we can’t notice at compile time but encounter on run time.

Solutions to Motivations

a. Just override methods and keep them empty if you don’t want to do anything with them.

If user can work without new method, Notify6(), this smells like a large interface problem. A segregation may be considered.

If you insist to have such a feature, you can design a callback register mechanism. Users can register any method they want. May register any of them, all of them or none of them. Keep function objects in the library, and callback registered functions. This seems a better OOP design compared to using inheritance.

b. Just avoid silent contract breaks. If an interface changes, it’s more secure to see compilation errors and solve them one by one at compile time.

Discussed design is currently in use in a widely used open source project. I’ve explained thoughts in my mind about it. All of them seems sensible to me.

I don’t think discussed motivations are huge gain. I can’t understand why does someone take the risks given at 1 and 4. Help me to understand advantages of such design. What am I missing here?

beginner – Object oriented Snake Javascript

I have implemented a little snake game with Javascript and Html but unfortunately it doesent really work.
It would be really nice if someone could look over it and give me some feedback about my mistakes.

The Snake Game:

class Snake
{
    constructor (x, y, pressedKey, cellSize, context)
    {
        this.x = x;
        this.y = y;
        this.pressedKey = pressedKey;
        this.cellSize = cellSize;
        this.context = context;
        this.body = ();

        document.addEventListener('keyup', this.handleKeyUp.bind(this))
    }

    handleKeyUp(event) 
    {
        if (event.code === 'ArrowRight' || event.code === 'ArrowLeft' || event.code === 'ArrowUp' || event.code === 'ArrowDown') 
        {
            this.pressedKey = event.code;
        }
    }

    updateBody()
    {
        if (this.body.length > 0)
        {
            this.body.pop();
            this.addBody();
        }
    }

    addBody()
    {
        this.body.unshift(new Body(this.x, this.y, this.cellSize));
    }

    changeDirection(newpressedKey)
    {
        this.pressedKey = newpressedKey;
    }

    move()
    {
        if (this.pressedKey === 'ArrowRight') 
        {
            this.x += 1;
        } 
        else if (this.pressedKey === 'ArrowDown') 
        {
            this.y += 1;
        } 
        else if (this.pressedKey === 'ArrowLeft') 
        {
            this.x -= 1;
        } 
        else if (this.pressedKey === 'ArrowUp') 
        {
            this.y -= 1;
        }
    }

    draw()
    {
        this.context.fillStyle = 'yellow';
        this.context.fillRect(this.x * this.cellSize, this.y * this.cellSize, this.cellSize, this.cellSize);

        //b wie body
        this.body.forEach(b => {
            this.context.fillStyle = 'black';
            this.context.fillRect(b.x * b.cellSize, b.y * b.cellSize, b.cellSize, b.cellSize);
        });
    }

    eatApple(apple)
    {
        return apple.x === this.x && apple.y === this.y;
    }

    eatSnake()
    {
        let gameOver = false;
        this.body.forEach(b => {
            if (b.x === this.x && b.y === this.y)
            gameOver = true;
        });
        return gameOver;
    }

    eatWall()
    {
        let gameOver = false;
        this.body.forEach(b => 
        {
            if (b.x < 0)
            {
                gameOver = true;
            }
            if (b.y < 0)
            {
                gameOver = true;
            }
            if (b.x > 8)
            {
                gameOver = true;
            }
            if (b.y > 8)
            {
                gameOver = true;
            }
        });
        return gameOver;
    }

    update()
    {
        let gameOver = false;
        gameOver = this.eatSnake() || this.eatWall();
        if (!gameOver)
        {
            if (this.eatApple(apple))
            {
                this.addBody();
                apple.newPosition();
            }
            this.updateBody();
            this.move();
        }
    }
}

class Body
{
    constructor(x, y, cellSize)
    {
        this.x = x;
        this.y = y;
        this.cellSize = cellSize;
    }
}

class Apple
{
    constructor(cellSize, context)
    {
        this.x = Math.floor(Math.random() * 8);
        this.y = Math.floor(Math.random() * 8);
        this.cellSize = cellSize;
        this.context = context; 
    }

    newPosition()
    {
        this.x = Math.floor(Math.random() * 8);
        this.y = Math.floor(Math.random() * 8);
    }

    draw()
    {
        this.context.fillStyle = 'red';
        this.context.fillRect(this.x * this.cellSize, this.y * this.cellSize, this.cellSize, this.cellSize);   
    }
}

class Game
{
    constructor(canvas, extent) 
    {
        this.canvas = canvas;
        this.context = this.canvas.getContext('2d');
        this.extent = extent;
        this.cellSize = this.canvas.width / this.extent;

        this.snake = new Snake(0, 0, 'ArrowDown', this.cellSize, this.context, this.extent);
        this.apple = new Apple(this.cellSize, this.context, this.extent)

        setInterval(this.loop.bind(this), 250);   
    }

    drawLine(x1, y1, x2, y2) 
    {
        this.context.beginPath();
        this.context.moveTo(x1, y1);
        this.context.lineTo(x2, y2);
        this.context.stroke();
    }

    drawGrid() 
    {
        for(let i = 1; i < this.extent; i++) 
        {
            this.drawLine(0, i * this.cellSize, this.canvas.width, i * this.cellSize);
            this.drawLine(i * this.cellSize, 0, i * this.cellSize, this.canvas.height);
        }
    }

    draw() 
    {
        this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);

        this.drawGrid();
        this.apple.draw();
        this.snake.draw();
    }

    update() 
    {
        this.snake.update();
    }

    loop() {
        this.update();
        this.draw();
    }
}
new Game(document.getElementById('myCanvas'), 8)

The HTML:

<!DOCTYPE html>
<html>
    <head>
        <title>test</title>
    </head>
    <body>
        <canvas id="myCanvas" width="640" height="640"></canvas>
        <script  type="text/javascript" src="https://codereview.stackexchange.com/main.js"></script>
    </body>
</html>

Object oriented design concepts vs Object Oriented design principles

What are the object oriented design concepts? Are they encapsulation, inheritance etc. ? I am confused of the difference between OOD concepts and principles? Are they the same? If they are different how do they relate?

object oriented – Simple State Machine and Transition Table

The goal is to have well defined state transitions, and the ability to provide the next event to execute.

I’d like to know if this is a proper implementation of State Machine, considering how states and transitionTable are defined, and how I handle event as input and output via update.

In many examples I cannot clearly define verbiage for state, so I defined them as verbs (notice ing suffix), as if it represents the ongoing progress of workflow. This may be wrong..

I also defined multiple events that could occur for a single state. For example, GetDeviceStatus and GetIntegrityStatus events both occur in RetrievingStatus state. This is also the case for Downloading state. You can see in the cases, when determining the next event, I need to check what the previous state was first.

If flawed, what are the pitfalls to my design, and how could it be improved? thanks.

enum ExampleState {
    case Initiate
    case Authorizing 
    case RetrievingStatus 
    case Downloading 
    case Confirming 
    case End
}

enum ExampleEvent {
    case InitiateSequence
    case GetAuthToken
    case GetDeviceStatus
    case GetIntegrityStatus
    case DownloadFromServer
    case DownloadToDevice
    case Confirm
}


class StateMachine {

var oldState: ExampleState!
var currentState: ExampleState!
var currentEvent: ExampleEvent!

var table: (ExampleState: (ExampleEvent: ExampleState)) = (.Initiate:         (.InitiateSequence:   .Authorizing),
                                                           .Authorizing:      (.GetAuthToken:       .RetrievingStatus),
                                                           .RetrievingStatus: (.GetDeviceStatus:    .Downloading, .GetIntegrityStatus: .Confirming),
                                                           .Downloading:      (.DownloadFromServer: .Downloading, .DownloadToDevice: .RetrievingStatus),
                                                           .Confirming:       (.Confirm:            .End))

func nextEvent(event: ExampleEvent) -> EventExecutor? {

    let transitionState = table(currentState)!(event)!
    let oldState = currentState

    switch (transitionState) {

        case .Initiate:
            currentState = .Initiate
            return InitiateSequence()

        case .Authorizing:
            currentState = .Authorizing
            return GetAuthToken()

        case .RetrievingStatus:
            currentState = .RetrievingStatus
            switch (oldState) {
            case .Authorizing: return GetDeviceStatus()
            case .Downloading: return GetIntegrityStatus()
            default: return nil
            }

        case .Downloading:
            currentState = .Downloading
            switch (oldState) {
            case .RetrievingStatus: return DownloadFromServer()
            case .Downloading: return DownloadToDevice()
            default: return nil
            }

        case .Confirming:
            currentState = .Confirming
            return Confirm()

        case .End:
            currentState = .End
            return nil
    }

}
}

Use case

@objc func rxExecute() {
    publisher
        .map { self.stateMachine.nextEvent(event: $0) } // $0 == event that just completed
        .flatMap { $0!.rxExecute() } // execute next event from state machine output
        .subscribe(subscriber)
}

object oriented – What is the right design pattern for me to abstract this class?

I am working with some code that takes advantage of ControllerContext.

See: https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.controllercontext?view=aspnet-mvc-5.2

My code looks something like this

public class GenerateAView()
{
    GenerateAView()
    {
    }

    RenderView()
    {
        RequestContext myRequestContext = ...
        ControllerBase myController = ....

        ControllerContext controllerContext = new ControllerContext(myRequestContext, myController);
        RenderToString()
    }

    RendertoString(ControllerContext controllerContext)
    {
        .....
    }
}

So my thoughts are.

1) I need a Factory for ControllerContexts, I see their are 3-4 Ctors on ControllerContext, so I would create a ControllerContextFactory, then pass it in to the GenerateAViewCtor, which would have a FactoryMethod for each ControllerContext Ctor. This I am pretty sure of.

2) I need a facade or some other wrapper pattern so I can mock this and write unit tests. This I am not as sure of.

  • I could just extend ControllerContext, then create an interface. Not sure what this pattern would be called.
  • I could write a legit wrapper, that just has a ControllerContext property. Not sure if this is a Facade, a Bridge, or a Adapter?