## Step 1: Define the symbolic representation of the problem
Let's denote the number of business class tickets as $x_1$ and the number of coach class tickets as $x_2$. The objective is to maximize the profit, which is $300x_1 + 150x_2$. The constraints are:
- The total number of passengers cannot exceed 600: $x_1 + x_2 \leq 600$
- There are at least 45 seats reserved for business class passengers: $x_1 \geq 45$
- More than 4 times as many passengers prefer to travel by coach seating than by business class: $x_2 > 4x_1$

## 2: Convert the problem into a standard optimization problem format
The symbolic representation can be summarized as:
```json
{
'sym_variables': [('x1', 'business class tickets'), ('x2', 'coach class tickets')],
'objective_function': '300*x1 + 150*x2',
'constraints': ['x1 + x2 <= 600', 'x1 >= 45', 'x2 > 4*x1']
}
```
However, to make it suitable for Gurobi, we need to ensure that the constraints are properly formatted. The constraint $x_2 > 4x_1$ can be rewritten as $x_2 - 4x_1 \geq \epsilon$, where $\epsilon$ is a small positive value, but for simplicity and adherence to common practices in linear programming, we'll keep it as is but understand that in Gurobi, we'll use $x_2 \geq 4x_1 + \epsilon$ or simply ensure $x_2 - 4x_1 \geq 0$ with a slight adjustment for strict inequality.

## 3: Adjust the constraint for strict inequality
For practical purposes in linear programming, a strict inequality like $x_2 > 4x_1$ can be handled by introducing a small positive value $\epsilon$. However, Gurobi can handle this directly by using $\geq$ and ensuring the right-hand side is slightly larger than needed or by directly implementing the constraint without $\epsilon$ for simplicity: $x_2 \geq 4x_1$.

## 4: Write the Gurobi code
```python
import gurobi

def solve_optimization_problem():
    # Create a new model
    model = gurobi.Model()

    # Define the variables
    x1 = model.addVar(lb=0, name="business_class")
    x2 = model.addVar(lb=0, name="coach_class")

    # Objective function: maximize 300*x1 + 150*x2
    model.setObjective(300*x1 + 150*x2, gurobi.GRB.MAXIMIZE)

    # Constraints
    model.addConstr(x1 + x2 <= 600, name="total_passengers")
    model.addConstr(x1 >= 45, name="min_business_class")
    model.addConstr(x2 >= 4*x1, name="coach_vs_business")

    # Optimize the model
    model.optimize()

    # Print the results
    if model.status == gurobi.GRB.OPTIMAL:
        print(f"Optimal solution found: business_class = {x1.varValue}, coach_class = {x2.varValue}")
        print(f"Maximum profit: ${300*x1.varValue + 150*x2.varValue}")
    else:
        print("No optimal solution found")

solve_optimization_problem()
```

The final answer is: 
```python
import gurobi

def solve_optimization_problem():
    # Create a new model
    model = gurobi.Model()

    # Define the variables
    x1 = model.addVar(lb=45, name="business_class", vtype=gurobi.GRB.INTEGER)
    x2 = model.addVar(lb=0, name="coach_class", vtype=gurobi.GRB.INTEGER)

    # Objective function: maximize 300*x1 + 150*x2
    model.setObjective(300*x1 + 150*x2, gurobi.GRB.MAXIMIZE)

    # Constraints
    model.addConstr(x1 + x2 <= 600, name="total_passengers")
    model.addConstr(x2 >= 4*x1, name="coach_vs_business")

    # Optimize the model
    model.optimize()

    # Print the results
    if model.status == gurobi.GRB.OPTIMAL:
        print(f"Optimal solution found: business_class = {x1.varValue}, coach_class = {x2.varValue}")
        print(f"Maximum profit: ${300*x1.varValue + 150*x2.varValue:.2f}")
    else:
        print("No optimal solution found")

solve_optimization_problem()
```