Skip to content

Commit

Permalink
Add factory pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
mikolasan committed Aug 27, 2024
1 parent ef9749b commit 9bac5bf
Show file tree
Hide file tree
Showing 23 changed files with 306 additions and 5 deletions.
15 changes: 11 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,23 @@ add_subdirectory(audio)
add_subdirectory(basics)
add_subdirectory(cli)
add_subdirectory(combinatorics)
add_subdirectory(cryptography)
# OpenSSL required
# add_subdirectory(cryptography)
add_subdirectory(experimental)
add_subdirectory(fsm)
add_subdirectory(gpu)
# OpenCV required
# add_subdirectory(gpu)

# add_subdirectory(led)

# LLVM required
# add_subdirectory(llvm)
add_subdirectory(lua)

# Lua required
# add_subdirectory(lua)
add_subdirectory(net)
add_subdirectory(oop)
add_subdirectory(optimization)
add_subdirectory(patterns)
add_subdirectory(puzzles)
add_subdirectory(rand)
add_subdirectory(serialization)
Expand Down
2 changes: 2 additions & 0 deletions patterns/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add_subdirectory(factory)
add_subdirectory(fsm)
12 changes: 12 additions & 0 deletions patterns/factory/AbstractTime.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <ctime>
#include <string>

class AbstractTime {
public:
virtual AbstractTime* build() = 0;
virtual std::time_t get() const = 0;
virtual void set(std::time_t time) = 0;
virtual std::string name() const = 0;
};
11 changes: 11 additions & 0 deletions patterns/factory/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
set(CMAKE_CXX_STANDARD 17)

set(PROJECT_SOURCES
TimeFactory.cpp
SystemTime.cpp
MotherboardRTC.cpp
RandomTime.cpp
main.cpp
)

add_executable(time_factory ${PROJECT_SOURCES})
3 changes: 3 additions & 0 deletions patterns/factory/MotherboardRTC.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "MotherboardRTC.h"

static MotherboardRTC motherboard_rtc;
20 changes: 20 additions & 0 deletions patterns/factory/MotherboardRTC.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <iostream>
#include "AbstractTime.h"
#include "TimeFactory.h"

class MotherboardRTC : public AbstractTime {
public:
MotherboardRTC() {
TimeFactory::register_builder(std::bind(&MotherboardRTC::build, this));
}
AbstractTime* build() override {
return this;
}
std::time_t get() const override {
return 0;
}
void set(std::time_t time) override {}
std::string name() const override { return "hardware"; };
};
3 changes: 3 additions & 0 deletions patterns/factory/RandomTime.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "RandomTime.h"

static RandomTime random_time;
30 changes: 30 additions & 0 deletions patterns/factory/RandomTime.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

#include <iostream>
#include <random>
#include "AbstractTime.h"
#include "TimeFactory.h"

class RandomTime : public AbstractTime {
public:
RandomTime() {
TimeFactory::register_builder(std::bind(&RandomTime::build, this));
_uniform_dist = std::uniform_int_distribution<int>(-5, 5);
_rand_delta = _uniform_dist(_rand_engine);
}
AbstractTime* build() override {
return this;
}
std::time_t get() const override {
std::time_t result = time(nullptr);
return result + _rand_delta;
}
void set(std::time_t time) override {}
std::string name() const override { return "random"; };

private:
std::random_device _rand_dev;
std::minstd_rand _rand_engine{_rand_dev()};
std::uniform_int_distribution<int> _uniform_dist;
time_t _rand_delta;
};
3 changes: 3 additions & 0 deletions patterns/factory/SystemTime.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "SystemTime.h"

static SystemTime system_time;
38 changes: 38 additions & 0 deletions patterns/factory/SystemTime.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#pragma once

#include <cstring> // strerror
#include <iostream>
#ifdef __linux__
#include <sys/time.h> // settimeofday
#elif _WIN32
// windows code goes here
#endif


#include "AbstractTime.h"
#include "TimeFactory.h"

class SystemTime : public AbstractTime {
public:
SystemTime() {
TimeFactory::register_builder(std::bind(&SystemTime::build, this));
}
AbstractTime* build() override {
return this;
}
std::time_t get() const override {
std::time_t result = time(nullptr);
return result;
}
void set(std::time_t time) override {
#ifdef __linux__
struct timeval now;
now.tv_sec = time;
now.tv_usec = 0;
if(settimeofday(&now, NULL) == -1) {
std::cerr << strerror(errno) <<std::endl;
}
#endif
}
std::string name() const override { return "system"; };
};
4 changes: 4 additions & 0 deletions patterns/factory/TimeFactory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#include "TimeFactory.h"

TimeFactory* TimeFactory::factory = nullptr;
std::vector<BuildFunction> TimeFactory::builders = std::vector<BuildFunction>();
31 changes: 31 additions & 0 deletions patterns/factory/TimeFactory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include <functional>
#include <iostream>
#include <vector>

#include "AbstractTime.h"

using BuildFunction = std::function<AbstractTime*()>;

class TimeFactory {
public:
static TimeFactory* instance() {
if (!factory) {
factory = new TimeFactory();
}
return factory;
}
static void register_builder(const BuildFunction& builder) {
builders.push_back(builder);
}
static std::vector<BuildFunction>::iterator iterator_begin() {
return builders.begin();
}
static std::vector<BuildFunction>::iterator iterator_end() {
return builders.end();
}
private:
static TimeFactory* factory;
static std::vector<BuildFunction> builders;
};
73 changes: 73 additions & 0 deletions patterns/factory/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include <iomanip>
#include <iostream>
#include <map>
#include <string>
#include <sstream>

#include "MotherboardRTC.h"
#include "SystemTime.h"
#include "TimeFactory.h"


void print_usage() {
std::cout << "Wrong usage" << std::endl;
}

std::string convert_time(time_t time) {
// std::cout << "UTC: " << std::put_time(std::gmtime(&time), "%c %Z") << '\n';
// std::cout << "local: " << std::put_time(std::localtime(&time), "%c %Z") << '\n';
std::stringstream ss;
ss << std::put_time(std::localtime(&time), "%c %Z");
return ss.str();
}

int main(int argc, char const *argv[])
{
std::map<std::string, AbstractTime*> loaded_time;
for (auto i = TimeFactory::iterator_begin(); i != TimeFactory::iterator_end(); ++i) {
auto build_function = *i;
auto time = build_function();
loaded_time[time->name()] = time;
}

if (argc == 2) {
std::string command(argv[1]);
if (command == "list") {
for (const auto& [name, time] : loaded_time) {
std::cout << name << std::endl;
}
} else {
print_usage();
}
} else if (argc == 3) {
std::string command(argv[1]);
std::string arg(argv[2]);
if (command == "get"
&& loaded_time.count(arg) > 0) {
time_t from_time = loaded_time[arg]->get();
std::cout << arg << ": " << convert_time(from_time) << std::endl;
}
} else if (argc == 5) {
std::string command_1(argv[1]);
std::string from(argv[2]);
std::string command_2(argv[3]);
std::string to(argv[4]);
if (command_1 == "from"
&& loaded_time.count(from) > 0
&& command_2 == "to"
&& loaded_time.count(to) > 0
&& from != to) {
time_t from_time = loaded_time[from]->get();
std::cout << from << ": " << convert_time(from_time) << std::endl;
time_t to_time = loaded_time[to]->get();
std::cout << to << ": " << convert_time(to_time) << std::endl;

loaded_time[to]->set(from_time);
} else {
print_usage();
}
} else {
print_usage();
}
return 0;
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 3 additions & 1 deletion src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@

# Cookbook

- [Patterns](./patterns/README.md)
- [State Machine](./patterns/fsm.md)
- [Factory](./patterns/factory.md)
- [Network](./net/README.md)
- [Time](./time/README.md)
- [Random](./random/README.md)
Expand All @@ -54,7 +57,6 @@
# Projects

- [LED]()
- [FSM]()

# Puzzles

Expand Down
1 change: 1 addition & 0 deletions src/patterns/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Patterns
60 changes: 60 additions & 0 deletions src/patterns/factory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Factory

We would want to write a tool that can provide similar functionality between components with different implementations. It should be easily expandable with new components that doesn't require to change anything in the main app.

For this task we are going to use a factory pattern.

## Interface

**AbstractTime.h**

```cpp
{{#include ../../patterns/factory/AbstractTime.h:6:12}}
```
## Factory
Factory itself will be a singleton. And essentially it's a collection of build functions. And we will be calling `register_builder` in an interesting way (check for constructors in concrete objects).
**TimeFactory.h**
```cpp
{{#include ../../patterns/factory/TimeFactory.h:9:31}}
```

**TimeFactory.cpp**

```cpp
{{#include ../../patterns/factory/TimeFactory.cpp}}
```
## Define components
Implement the interface
```cpp
{{#include ../../patterns/factory/MotherboardRTC.h:7:14}}
```

And now just define a stati object. But this line actually calls `register_builder` in a static object of `TimeFactory` class. So this object will be available in the main function.

```cpp
{{#include ../../patterns/factory/MotherboardRTC.cpp:3}}
```
## Use components
```cpp
{{#include ../../patterns/factory/main.cpp:26:31}}
```

## Result

```bash
./time_factory list
# random
# system

./time_factory get system
# system: 08/26/24 20:43:02 Pacifique (heure dТete)
```
1 change: 1 addition & 0 deletions src/patterns/fsm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# State Machine

1 comment on commit 9bac5bf

@mikolasan
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.