Nice programing

Mixins는 무엇입니까 (개념으로)

nicepro 2020. 11. 4. 08:24
반응형

Mixins는 무엇입니까 (개념으로)


나는 Mixin 개념에 대해 머리를 돌리려고 노력하고 있지만 그것이 무엇인지 이해하지 못하는 것 같습니다. 내가 보는 방식은 상속을 사용하여 클래스의 기능을 확장하는 방법이라는 것입니다. 나는 사람들이 그것들을 "추상 하위 클래스"라고 부르는 것을 읽었습니다. 아무도 이유를 설명 할 수 있습니까?

다음 예 (제 강의 슬라이드 쇼 중 하나에서)를 기반으로 답변을 설명해 주시면 감사하겠습니다. C ++ Mixin 예제


믹스 인이 무엇인지 알아보기 전에 해결하려는 문제를 설명하는 것이 유용합니다. 모델링하려는 아이디어 나 개념이 많이 있다고 가정 해 보겠습니다. 어떤 식 으로든 관련이있을 수 있지만 대부분 직교합니다. 즉, 서로 독립적으로 설 수 있습니다. 이제 상속을 통해이를 모델링하고 이러한 각 개념이 일부 공통 인터페이스 클래스에서 파생되도록 할 수 있습니다. 그런 다음 해당 인터페이스를 구현하는 파생 클래스에 구체적인 메서드를 제공합니다.

이 접근 방식의 문제점은이 디자인이 각각의 구체적인 클래스를 가져 와서 결합하는 명확하고 직관적 인 방법을 제공하지 않는다는 것입니다.

믹스 인의 아이디어는 각기 기본 직교 개념을 모델링하는 여러 개의 기본 클래스를 제공하고, 레고와 같이 원하는 기능만으로 더 복잡한 클래스를 구성하기 위해 서로 붙일 수 있도록하는 것입니다. 기본 클래스 자체는 빌딩 블록으로 사용됩니다. 나중에 기존 클래스에 영향을주지 않고 컬렉션에 다른 기본 클래스를 추가 할 수 있기 때문에 확장 가능합니다.

C ++로 돌아가서이를 수행하는 기술은 템플릿과 상속을 사용하는 것입니다. 여기서 기본 아이디어는 템플릿 매개 변수를 통해 제공하여 이러한 빌딩 블록을 함께 연결하는 것입니다. 그런 다음 함께 연결합니다. 를 통해 typedef원하는 기능을 포함하는 새 유형을 형성합니다.

예를 들어 다시 실행 기능을 추가하고 싶다고 가정 해 보겠습니다. 다음과 같이 보일 수 있습니다.

#include <iostream>
using namespace std;

struct Number
{
  typedef int value_type;
  int n;
  void set(int v) { n = v; }
  int get() const { return n; }
};

template <typename BASE, typename T = typename BASE::value_type>
struct Undoable : public BASE
{
  typedef T value_type;
  T before;
  void set(T v) { before = BASE::get(); BASE::set(v); }
  void undo() { BASE::set(before); }
};

template <typename BASE, typename T = typename BASE::value_type>
struct Redoable : public BASE
{
  typedef T value_type;
  T after;
  void set(T v) { after = v; BASE::set(v); }
  void redo() { BASE::set(after); }
};

typedef Redoable< Undoable<Number> > ReUndoableNumber;

int main()
{
  ReUndoableNumber mynum;
  mynum.set(42); mynum.set(84);
  cout << mynum.get() << '\n';  // 84
  mynum.undo();
  cout << mynum.get() << '\n';  // 42
  mynum.redo();
  cout << mynum.get() << '\n';  // back to 84
}

원본에서 몇 가지 사항을 변경했음을 알 수 있습니다.

  • 컴파일 타임에 구성된 클래스 유형이 무엇인지 정확히 알고 있기 때문에 가상 함수가 실제로 필요하지 않습니다.
  • value_type두 번째 템플릿 매개 변수에 대한 기본값 추가하여 사용을 덜 번거롭게했습니다. 이렇게 <foobar, int>하면 조각을 함께 붙일 때마다 계속 타이핑 할 필요가 없습니다 .
  • 조각에서 상속되는 새 클래스를 만드는 대신 단순 typedef이 사용됩니다.

이것은 믹스 인 아이디어를 설명하기위한 간단한 예입니다. 따라서 코너 케이스와 재미있는 사용법을 고려하지 않습니다. 예를 들어, undo숫자를 설정하지 않고 수행하면 예상대로 작동하지 않을 수 있습니다.

참고 로이 기사가 도움 될 수도 있습니다.


mixin은 일반적으로 기능에 필요한 기본 기능을 제공하는 지정된 클래스를 통해 다른 클래스에 대한 기능을 제공하기 위해 지정된 클래스입니다. 예를 들면 다음과 같습니다
.이 경우 mixin은 값 클래스의 설정 작업을 취소하는 기능을 제공합니다. 이 hability는 get/set매개 변수화 된 클래스 ( Number귀하의 예에서는 클래스)에서 제공 하는 기능을 기반으로합니다 .

(에서 추출 또 다른 예를 들어 " C의 믹스 인 기반 프로그래밍 ++ " ) :

template <class Graph>
class Counting: public Graph {
  int nodes_visited, edges_visited;
public:
  Counting() : nodes_visited(0), edges_visited(0), Graph() { }
  node succ_node (node v) {
    nodes_visited++;
    return Graph::succ_node(v);
  }
  edge succ_edge (edge e) {
    edges_visited++;
    return Graph::succ_edge(e);
  }
... 
};

이 예에서 mixin은 순회 작업을 수행하는 그래프 클래스가 주어지면 정점 수 계산 기능을 제공합니다 .

일반적으로 C ++에서 믹스 인은 CRTP 관용구를 통해 구현됩니다 . 이 스레드는 C ++의 mixin 구현에 대한 좋은 읽기가 될 수 있습니다. C ++ Mixin-Style이란 무엇입니까?

다음은 CRTP 관용구를 이용하는 믹스 인의 예입니다 (@Simple에게 감사드립니다) :

#include <cassert>
#ifndef NDEBUG
#include <typeinfo>
#endif

class shape
{
public:
    shape* clone() const
    {
        shape* const p = do_clone();
        assert(p && "do_clone must not return a null pointer");
        assert(
            typeid(*p) == typeid(*this)
            && "do_clone must return a pointer to an object of the same type"
        );
        return p;
    }

private:
    virtual shape* do_clone() const = 0;
};

template<class D>
class cloneable_shape : public shape
{
private:
    virtual shape* do_clone() const
    {
        return new D(static_cast<D&>(*this));
    }
};

class triangle : public cloneable_shape<triangle>
{
};

class square : public cloneable_shape<square>
{
};

이 믹스 인은 셰이프 클래스 집합 (계층 구조)에 이기종 복사 기능을 제공합니다 .


나는 그레이트 울프의 대답을 좋아하지만 한 가지주의 할 점이있다.

greatwolf stated, "The virtual functions really aren't necessary here because we know exactly what our composed class type is at compile-time." Unfortunately, you can run into some inconsistent behavior if you use your object polymorphically.

Let me tweak the main function from his example:

int main()
{
  ReUndoableNumber mynum;
  Undoable<Number>* myUndoableNumPtr = &mynum;

  mynum.set(42);                // Uses ReUndoableNumber::set
  myUndoableNumPtr->set(84);    // Uses Undoable<Number>::set (ReUndoableNumber::after not set!)
  cout << mynum.get() << '\n';  // 84
  mynum.undo();
  cout << mynum.get() << '\n';  // 42
  mynum.redo();
  cout << mynum.get() << '\n';  // OOPS! Still 42!
}  

By making the "set" function virtual, the proper override will be called and the inconsistent behavior above will not occur.


Mixins in C++ are expressed using the Curiously Recurring Template Pattern (CRTP). This post is an excellent breakdown of what they provide over other reuse techniques... compile-time polymorphism.


This works the same as an interface and maybe more so as an abstract, but interfaces are easier to get first time.

It addresses many issues but one I find in development that comes up a lot is external apis. imagine this.

You have a database of users, that database has a certain way of getting access to its data. now imagine you have facebook, that also has a certain way of getting access to its data (api).

at any point your application may need to run using data from facebook or your database. so what you do is create an interface that says "anything that implements me is going to definitely have the following methods" now you can implement that interface into your application...

because an interface promises that the implementing repositories will have the methods declared in them, you know that wherever or whenever you use that interface in your application, if you switch the data over, it's always going to have the methods you are defining and thus have data to work off of.

There are many more layers to this pattern of working, but the essence is that it is good because data or other such persistant items become a big part of your application, and if they change without you knowing, your application can break :)

Here's some pseudo code.

interface IUserRepository
{
    User GetUser();
}

class DatabaseUserRepository : IUserRepository
{
    public User GetUser()
    {
        // Implement code for database
    }
}

class FacebookUserRepository : IUserRepository
{
    public User GetUser()
    {
        // Implement code for facebook
    }
}

class MyApplication
{
    private User user;

    MyApplication( IUserRepository repo )
    {
        user = repo;
    }
}

// your application can now trust that user declared in private scope to your application, will have access to a GetUser method, because if it isn't the interface will flag an error.

To understand the concept forget classes for a moment. Think (most popular) JavaScript. Where objects are dynamic arrays of methods and properties. Callable by their name as a symbol or as a string literal. How would you implement that in standard C++ in a year 2018? Not easily. But that is the core of the concept. In JavaScript one can add and remove (aka mix-in) whenever and whatever one wishes to. Very important: No class inheritance.

Now onto C++. Standard C++ has all you need, does not help as a statement here. Obviously I will not write a scripting language in order to implement mix-in using C++.

Yes, this is a good article , but for inspiration only. CRTP is not a panacea. And also the so called academic approach is here, also (in essence) CRTP based.

이 답변을 하향 투표하기 전에 아마도 지팡이 상자의poc 코드를 고려하십시오 :)

참고 URL : https://stackoverflow.com/questions/18773367/what-are-mixins-as-a-concept

반응형